home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / dviware / vutex / vutex.web (.txt) < prev   
Texinfo Document  |  1990-10-01  |  128KB  |  3,106 lines

  1. % vutex:web
  2. %  This program was written by Warren Wolfe, and all rights are reserved.
  3. %  Copyright 1987 CUBE Software, Victoria, B.C., Canada.
  4. % Version 1.00  ASCII terminal driver:  December 1987
  5. %         1.01  Modification for horizontal and vertical shifts: June 1989
  6. %         1.02  Modification for page action: November 1989
  7. % Here is TeX material that gets inserted after \input webmac
  8. \def\hang{\hangindent 3em\indent\ignorespaces}
  9. \font\ninerm=amr9
  10. \let\mc=\ninerm % medium caps for names like PASCAL
  11. \def\PASCAL{{\mc PASCAL}}
  12. \def\vutex{{\bf vu\TeX}}
  13. \def\tamu{Texas A\char38 M}
  14. \def\(#1){} % this is used to make section names sort themselves better
  15. \def\9#1{} % this is used for sort keys in the index
  16. \def\title{{\tenrm \vutex}}
  17. \def\contentspagenumber{0}
  18. \def\topofcontents{\null
  19.   \def\titlepage{F} % include headline on the contents page
  20.   \def\rheader{\mainfont\hfil \contentspagenumber}
  21.   \vfill
  22.   \centerline{\titlefont The {\ttitlefont \vutex} processor}
  23.   \vskip 15pt
  24.   \centerline{(Version 1.02, Dec 1989)}
  25.   \vfill}
  26. \def\botofcontents{
  27. \bigskip
  28. \centerline{This report was written by Warren Wolfe.}
  29. \centerline{based on the original by Tomas Rokicki}
  30. \centerline{\copyright 1988}
  31. \pageno=\contentspagenumber \advance\pageno by 1
  32. @* Introduction.
  33. This report documents the program designed to output \TeX\ processed data to any
  34. ASCII terminal.  The program is based on \.{DVIgen} which is intended as a
  35. generic driver for \.{DVI} files created by \TeX82 and other document
  36. preparation systems and much of this documentation is extracted directly from
  37. the Rokicki original.  It should be relatively easy to modify for a particular
  38. system.
  39. \vutex\ uses the 95 printable ASCII characters and the rather limited row and
  40. column format of an ordinary screen or printer to mimic somehow the typeset
  41. output of \TeX\ and fine resolution printers.  Understandably the result is much
  42. less than other \.{DVIgen} processors, but it is a lot more acessible.
  43. The overall plan of \vutex\ is to scan the \.{DVI} file and to determine what
  44. fonts are being used.  For most of these fonts, a table is available which maps
  45. a character in the font to a corresponding printable ASCII character.  Then,
  46. with each page, a page grid is created corresponding to the raster grid of the
  47. screen or printer.  Each character is placed in a location in that grid
  48. according to the horizontal and vertical coordinates determined by \TeX.
  49. Subscripts or superscripts are associated with their baseline.  Finally the page
  50. grid is output to the device in either of two forms --- compressed or \TeX\
  51. spaced.  In the compressed mode, all extraneous spaces between words are
  52. deleted, while the alternate mode places words so that the characters are
  53. left-justified in the space and at the location determined by \TeX.
  54. @d clone=='vutex'
  55. @ The |banner| string defined here should be changed whenever \vutex\
  56. gets modified.
  57. @d banner=='This is ',clone,', Version 1.02'
  58.          {printed when the program starts}
  59. @ This program is written in standard \PASCAL, except where it is necessary
  60. to use extensions; for example, \vutex\ must read files whose names
  61. are dynamically specified, and that would be impossible in pure \PASCAL.
  62. All places where nonstandard constructions are used have been listed in
  63. the index under ``system dependencies.''
  64. @!@^system dependencies@>
  65. @d othercases == others: {default for cases not listed explicitly}
  66. @d endcases == @+end {follows the default case in an extended |case| statement}
  67. @f othercases == else
  68. @f endcases == end
  69. @ The binary input comes from |dvi_file|, and the final output is written
  70. to |bit_file|.  On line interaction and error messages are written
  71. on \PASCAL's standard |output| file. The term |print| is used instead of
  72. |write| when this program writes on |output|, so that all such output
  73. could easily be redirected if desired.
  74. @d print(#)==write(#)
  75. @d print_ln(#)==write_ln(#)
  76. @p program vutex(dvi_file,bit_file,input,output);
  77. label @<Labels in the outer block@>@/
  78. const @<Constants in the outer block@>@/
  79. type @<Types in the outer block@>@/
  80. var @<Globals in the outer block@>@/
  81. procedure initialize; {this procedure gets things started properly}
  82.   var i:integer; {loop index for initializations}
  83.   begin print_ln(banner);@/
  84.   @<Set initial values@>@/
  85.   end;
  86. @ If the program has to stop prematurely, it goes to the
  87. `|final_end|'. Another label, |done|, is used when stopping normally.
  88. @d final_end=9999 {label for the end of it all}
  89. @d done=30 {go here when finished with a subtask}
  90. @d pdone=31 {go here when finished with a subtask during prescan}
  91. @<Labels...@>=final_end,done,pdone;
  92. @ The following parameters can be changed at compile time to extend or
  93. reduce \vutex 's capacity.
  94. @<Constants...@>=
  95. @!max_mem_size=68000; {the major array used for almost everything.}
  96. @!name_size=1000; {total length of all font file names, special commands, and
  97. other miscellaneous strings.}
  98. @!terminal_line_length=150; {maximum number of characters input in a single
  99.   line of input from the terminal}
  100. @!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth}
  101. @!name_length=50; {maximum length of a file name}
  102. @!total_rast=56000; {maximum number of rasters on page}
  103. @!max_p_width=280;  {maximum width of page in rasters}
  104. @!dfl_p_width=200;  {default width of page in rasters}
  105. @!dfl_n_lines=280;  {maximum number of lines in a page }
  106. @!dfl_screen_width=80;  {default width of screen in rasters}
  107. @!dfl_screen_height=22;  {default height of screen in rasters}
  108. @!hor_overlap=6;  {overlap for horizontal page action}
  109. @!vert_overlap=2;  {overlap for vertical page action}
  110. @!page_length=36; {maximum physical length of a page  (in cm)}
  111. @!hh_offset=18; {offset at left side of page}
  112. @!vv_offset=36; {offset at top of page}
  113. @ Here are some macros for common programming idioms.
  114. @d incr(#) == #:=#+1 {increase a variable by unity}
  115. @d decr(#) == #:=#-1 {decrease a variable by unity}
  116. @d do_nothing == {empty statement}
  117. @ If the \.{DVI} file is badly malformed, the whole process must be aborted;
  118. \vutex\ will give up, after issuing an error message about the symptoms
  119. that were noticed.
  120. Such errors might be discovered inside of subroutines inside of subroutines,
  121. so a procedure called |jump_out| has been introduced. This procedure, which
  122. simply transfers control to the label |final_end| at the end of the program,
  123. contains the only non-local |goto| statement in \vutex.
  124. @^system dependencies@>
  125. @d abort(#)==begin print_ln(' ',#); jump_out;
  126.     end
  127. @d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
  128. @.Bad DVI file@>
  129. @p procedure jump_out;
  130. begin goto final_end;
  131. @* The character set.
  132. Like all programs written with the  \.{WEB} system, \vutex\ can be
  133. used with any character set. But it uses ASCII code internally, because
  134. the programming for portable input-output is easier when a fixed internal
  135. code is used, and because \.{DVI} files use ASCII code for file names
  136. and certain other strings.
  137. @d dfl_chr == '#' {default character ASCII code for unprintable fonts}
  138. @<Types...@>=
  139. @!ASCII_code=" ".."~"; {a subrange of the integers}
  140. @ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
  141. character sets were common, so it did not make provision for lower case
  142. letters. Nowadays, of course, we need to deal with both upper and lower case
  143. alphabets in a convenient way, especially in a program like \vutex,
  144. So we shall assume that the \PASCAL\ system being used for \vutex\
  145. has a character set containing at least the standard visible characters
  146. of ASCII code (|"!"| through |"~"|).
  147. Some \PASCAL\ compilers use the original name |char| for the data type
  148. associated with the characters in text files, while other \PASCAL s
  149. consider |char| to be a 64-element subrange of a larger data type that has
  150. some other name.  In order to accommodate this difference, we shall use
  151. the name |text_char| to stand for the data type of the characters in the
  152. output file.  We shall also assume that |text_char| consists of
  153. the elements |chr(first_text_char)| through |chr(last_text_char)|,
  154. inclusive. The following definitions should be adjusted if necessary.
  155. @^system dependencies@>
  156. @d text_char == char {the data type of characters in text files}
  157. @d first_text_char=0 {ordinal number of the smallest element of |text_char|}
  158. @d last_text_char=127 {ordinal number of the largest element of |text_char|}
  159. @<Types...@>=
  160. @!text_file=packed file of text_char;
  161. @ The \vutex\ processor converts between ASCII code and the user's external
  162. character set by means of arrays |xord| and |xchr| that are analogous to
  163. \PASCAL's |ord| and |chr| functions.  However, the fonts used by \TeX\ do not
  164. all use the same character table and so we define other arrays to handle output
  165. for the major font families.
  166. @<Globals...@>=
  167. @!xord: array [text_char] of ASCII_code;
  168.   {specifies conversion of input characters}
  169. @!xchr: array [0..255] of text_char;
  170.   {specifies conversion of output characters for tty fonts}
  171. @!rchr: array [0..255] of text_char;
  172.   {specifies conversion of output characters for roman type fonts}
  173. @!mchr: array [0..255] of text_char;
  174.   {specifies conversion of output characters for math italic fonts}
  175. @!schr: array [0..255] of text_char;
  176.   {specifies conversion of output characters for math symbol fonts}
  177. @!echr: array [0..255] of text_char;
  178.   {specifies conversion of output characters for math extension fonts}
  179. @ Under our assumption that the visible characters of standard ASCII are
  180. all present, the following assignment statements initialize the
  181. |xchr| array properly, without needing any system-dependent changes.
  182. @<Set init...@>=
  183. for i:=0 to @'12 do xchr[i]:=dfl_chr;
  184. xchr[@'13]:='^';
  185. xchr[@'14]:='v';
  186. xchr[@'15]:='''';
  187. xchr[@'16]:=dfl_chr;
  188. xchr[@'17]:=dfl_chr;@/
  189. xchr[@'20]:='i';
  190. xchr[@'21]:='j';
  191. xchr[@'22]:='`';
  192. xchr[@'23]:='''';
  193. xchr[@'24]:=' ';
  194. xchr[@'25]:=' ';
  195. xchr[@'26]:=' ';
  196. xchr[@'27]:=' ';@/
  197. xchr[@'30]:=' ';
  198. xchr[@'31]:=dfl_chr;
  199. xchr[@'32]:=dfl_chr;
  200. xchr[@'33]:=dfl_chr;
  201. xchr[@'34]:=dfl_chr;
  202. xchr[@'35]:=dfl_chr;
  203. xchr[@'36]:=dfl_chr;
  204. xchr[@'37]:=dfl_chr;
  205. xchr[@'40]:=' ';
  206. xchr[@'41]:='!';
  207. xchr[@'42]:='"';
  208. xchr[@'43]:='#';
  209. xchr[@'44]:='$';
  210. xchr[@'45]:='%';
  211. xchr[@'46]:='&';
  212. xchr[@'47]:='''';@/
  213. xchr[@'50]:='(';
  214. xchr[@'51]:=')';
  215. xchr[@'52]:='*';
  216. xchr[@'53]:='+';
  217. xchr[@'54]:=',';
  218. xchr[@'55]:='-';
  219. xchr[@'56]:='.';
  220. xchr[@'57]:='/';@/
  221. xchr[@'60]:='0';
  222. xchr[@'61]:='1';
  223. xchr[@'62]:='2';
  224. xchr[@'63]:='3';
  225. xchr[@'64]:='4';
  226. xchr[@'65]:='5';
  227. xchr[@'66]:='6';
  228. xchr[@'67]:='7';@/
  229. xchr[@'70]:='8';
  230. xchr[@'71]:='9';
  231. xchr[@'72]:=':';
  232. xchr[@'73]:=';';
  233. xchr[@'74]:='<';
  234. xchr[@'75]:='=';
  235. xchr[@'76]:='>';
  236. xchr[@'77]:='?';@/
  237. xchr[@'100]:='@@';
  238. xchr[@'101]:='A';
  239. xchr[@'102]:='B';
  240. xchr[@'103]:='C';
  241. xchr[@'104]:='D';
  242. xchr[@'105]:='E';
  243. xchr[@'106]:='F';
  244. xchr[@'107]:='G';@/
  245. xchr[@'110]:='H';
  246. xchr[@'111]:='I';
  247. xchr[@'112]:='J';
  248. xchr[@'113]:='K';
  249. xchr[@'114]:='L';
  250. xchr[@'115]:='M';
  251. xchr[@'116]:='N';
  252. xchr[@'117]:='O';@/
  253. xchr[@'120]:='P';
  254. xchr[@'121]:='Q';
  255. xchr[@'122]:='R';
  256. xchr[@'123]:='S';
  257. xchr[@'124]:='T';
  258. xchr[@'125]:='U';
  259. xchr[@'126]:='V';
  260. xchr[@'127]:='W';@/
  261. xchr[@'130]:='X';
  262. xchr[@'131]:='Y';
  263. xchr[@'132]:='Z';
  264. xchr[@'133]:='[';
  265. xchr[@'134]:='\';
  266. xchr[@'135]:=']';
  267. xchr[@'136]:='^';
  268. xchr[@'137]:='_';@/
  269. xchr[@'140]:='`';
  270. xchr[@'141]:='a';
  271. xchr[@'142]:='b';
  272. xchr[@'143]:='c';
  273. xchr[@'144]:='d';
  274. xchr[@'145]:='e';
  275. xchr[@'146]:='f';
  276. xchr[@'147]:='g';@/
  277. xchr[@'150]:='h';
  278. xchr[@'151]:='i';
  279. xchr[@'152]:='j';
  280. xchr[@'153]:='k';
  281. xchr[@'154]:='l';
  282. xchr[@'155]:='m';
  283. xchr[@'156]:='n';
  284. xchr[@'157]:='o';@/
  285. xchr[@'160]:='p';
  286. xchr[@'161]:='q';
  287. xchr[@'162]:='r';
  288. xchr[@'163]:='s';
  289. xchr[@'164]:='t';
  290. xchr[@'165]:='u';
  291. xchr[@'166]:='v';
  292. xchr[@'167]:='w';@/
  293. xchr[@'170]:='x';
  294. xchr[@'171]:='y';
  295. xchr[@'172]:='z';
  296. xchr[@'173]:='{';
  297. xchr[@'174]:='|';
  298. xchr[@'175]:='}';
  299. xchr[@'176]:='~';
  300. for i:=@'177 to 255 do xchr[i]:=dfl_chr;
  301. @ Many of the \TeX\ standard fonts use a character layout slightly different
  302. from that given above.  Therefore, we define a new array to handle output
  303. from these fonts. Modern Roman fonts are given below.
  304. @<Set init...@>=
  305. for i:=0 to @'17 do rchr[i]:=dfl_chr;
  306. rchr[@'20]:='i';
  307. rchr[@'21]:='j';
  308. rchr[@'22]:='`';
  309. rchr[@'23]:='''';
  310. rchr[@'24]:=' ';
  311. rchr[@'25]:=' ';
  312. rchr[@'26]:=' ';
  313. rchr[@'27]:=' ';@/
  314. rchr[@'30]:=' ';
  315. rchr[@'31]:='s';
  316. rchr[@'32]:='a';
  317. rchr[@'33]:='o';
  318. rchr[@'34]:='o';
  319. rchr[@'35]:='A';
  320. rchr[@'36]:='O';
  321. rchr[@'37]:='O';@/
  322. rchr[@'40]:=' ';
  323. rchr[@'41]:='!';
  324. rchr[@'42]:='"';
  325. rchr[@'43]:='#';
  326. rchr[@'44]:='$';
  327. rchr[@'45]:='%';
  328. rchr[@'46]:='&';
  329. rchr[@'47]:='''';@/
  330. rchr[@'50]:='(';
  331. rchr[@'51]:=')';
  332. rchr[@'52]:='*';
  333. rchr[@'53]:='+';
  334. rchr[@'54]:=',';
  335. rchr[@'55]:='-';
  336. rchr[@'56]:='.';
  337. rchr[@'57]:='/';@/
  338. rchr[@'60]:='0';
  339. rchr[@'61]:='1';
  340. rchr[@'62]:='2';
  341. rchr[@'63]:='3';
  342. rchr[@'64]:='4';
  343. rchr[@'65]:='5';
  344. rchr[@'66]:='6';
  345. rchr[@'67]:='7';@/
  346. rchr[@'70]:='8';
  347. rchr[@'71]:='9';
  348. rchr[@'72]:=':';
  349. rchr[@'73]:=';';
  350. rchr[@'74]:=dfl_chr;
  351. rchr[@'75]:='=';
  352. rchr[@'76]:=dfl_chr;
  353. rchr[@'77]:='?';@/
  354. rchr[@'100]:='@@';
  355. rchr[@'101]:='A';
  356. rchr[@'102]:='B';
  357. rchr[@'103]:='C';
  358. rchr[@'104]:='D';
  359. rchr[@'105]:='E';
  360. rchr[@'106]:='F';
  361. rchr[@'107]:='G';@/
  362. rchr[@'110]:='H';
  363. rchr[@'111]:='I';
  364. rchr[@'112]:='J';
  365. rchr[@'113]:='K';
  366. rchr[@'114]:='L';
  367. rchr[@'115]:='M';
  368. rchr[@'116]:='N';
  369. rchr[@'117]:='O';@/
  370. rchr[@'120]:='P';
  371. rchr[@'121]:='Q';
  372. rchr[@'122]:='R';
  373. rchr[@'123]:='S';
  374. rchr[@'124]:='T';
  375. rchr[@'125]:='U';
  376. rchr[@'126]:='V';
  377. rchr[@'127]:='W';@/
  378. rchr[@'130]:='X';
  379. rchr[@'131]:='Y';
  380. rchr[@'132]:='Z';
  381. rchr[@'133]:='[';
  382. rchr[@'134]:='"';
  383. rchr[@'135]:=']';
  384. rchr[@'136]:='^';
  385. rchr[@'137]:=' ';@/
  386. rchr[@'140]:='`';
  387. rchr[@'141]:='a';
  388. rchr[@'142]:='b';
  389. rchr[@'143]:='c';
  390. rchr[@'144]:='d';
  391. rchr[@'145]:='e';
  392. rchr[@'146]:='f';
  393. rchr[@'147]:='g';@/
  394. rchr[@'150]:='h';
  395. rchr[@'151]:='i';
  396. rchr[@'152]:='j';
  397. rchr[@'153]:='k';
  398. rchr[@'154]:='l';
  399. rchr[@'155]:='m';
  400. rchr[@'156]:='n';
  401. rchr[@'157]:='o';@/
  402. rchr[@'160]:='p';
  403. rchr[@'161]:='q';
  404. rchr[@'162]:='r';
  405. rchr[@'163]:='s';
  406. rchr[@'164]:='t';
  407. rchr[@'165]:='u';
  408. rchr[@'166]:='v';
  409. rchr[@'167]:='w';@/
  410. rchr[@'170]:='x';
  411. rchr[@'171]:='y';
  412. rchr[@'172]:='z';
  413. rchr[@'173]:='-';
  414. rchr[@'174]:='-';
  415. rchr[@'175]:='"';
  416. rchr[@'176]:='~';
  417. for i:=@'177 to 255 do rchr[i]:=dfl_chr;
  418. @ The standard \TeX\ math italics fonts use quite a different layout than
  419. either of the above, and we include a separate array for these useful fonts.
  420. @<Set init...@>=
  421. for i:=0 to @'47 do mchr[i]:=dfl_chr;
  422. mchr[@'50]:='-';
  423. mchr[@'51]:='-';
  424. mchr[@'52]:='-';
  425. mchr[@'53]:='-';
  426. mchr[@'54]:=' ';
  427. mchr[@'55]:=' ';
  428. mchr[@'56]:='>';
  429. mchr[@'57]:='<';@/
  430. mchr[@'60]:='0';
  431. mchr[@'61]:='1';
  432. mchr[@'62]:='2';
  433. mchr[@'63]:='3';
  434. mchr[@'64]:='4';
  435. mchr[@'65]:='5';
  436. mchr[@'66]:='6';
  437. mchr[@'67]:='7';@/
  438. mchr[@'70]:='8';
  439. mchr[@'71]:='9';
  440. mchr[@'72]:='.';
  441. mchr[@'73]:=',';
  442. mchr[@'74]:='<';
  443. mchr[@'75]:='/';
  444. mchr[@'76]:='>';
  445. mchr[@'77]:='*';@/
  446. mchr[@'100]:=dfl_chr;
  447. mchr[@'101]:='A';
  448. mchr[@'102]:='B';
  449. mchr[@'103]:='C';
  450. mchr[@'104]:='D';
  451. mchr[@'105]:='E';
  452. mchr[@'106]:='F';
  453. mchr[@'107]:='G';@/
  454. mchr[@'110]:='H';
  455. mchr[@'111]:='I';
  456. mchr[@'112]:='J';
  457. mchr[@'113]:='K';
  458. mchr[@'114]:='L';
  459. mchr[@'115]:='M';
  460. mchr[@'116]:='N';
  461. mchr[@'117]:='O';@/
  462. mchr[@'120]:='P';
  463. mchr[@'121]:='Q';
  464. mchr[@'122]:='R';
  465. mchr[@'123]:='S';
  466. mchr[@'124]:='T';
  467. mchr[@'125]:='U';
  468. mchr[@'126]:='V';
  469. mchr[@'127]:='W';@/
  470. mchr[@'130]:='X';
  471. mchr[@'131]:='Y';
  472. mchr[@'132]:='Z';
  473. mchr[@'133]:=dfl_chr;
  474. mchr[@'134]:=dfl_chr;
  475. mchr[@'135]:=dfl_chr;
  476. mchr[@'136]:=dfl_chr;
  477. mchr[@'137]:=dfl_chr;@/
  478. mchr[@'140]:='l';
  479. mchr[@'141]:='a';
  480. mchr[@'142]:='b';
  481. mchr[@'143]:='c';
  482. mchr[@'144]:='d';
  483. mchr[@'145]:='e';
  484. mchr[@'146]:='f';
  485. mchr[@'147]:='g';@/
  486. mchr[@'150]:='h';
  487. mchr[@'151]:='i';
  488. mchr[@'152]:='j';
  489. mchr[@'153]:='k';
  490. mchr[@'154]:='l';
  491. mchr[@'155]:='m';
  492. mchr[@'156]:='n';
  493. mchr[@'157]:='o';@/
  494. mchr[@'160]:='p';
  495. mchr[@'161]:='q';
  496. mchr[@'162]:='r';
  497. mchr[@'163]:='s';
  498. mchr[@'164]:='t';
  499. mchr[@'165]:='u';
  500. mchr[@'166]:='v';
  501. mchr[@'167]:='w';@/
  502. mchr[@'170]:='x';
  503. mchr[@'171]:='y';
  504. mchr[@'172]:='z';
  505. mchr[@'173]:='i';
  506. mchr[@'174]:='j';
  507. mchr[@'175]:='p';
  508. mchr[@'176]:=' ';
  509. mchr[@'177]:=' ';@/
  510. for i:=@'200 to 255 do mchr[i]:=dfl_chr;
  511. @ The math symbol font is, for the most part, unprintable in ASCII, but we
  512. shall map some of the characters onto meaningful representations.
  513. @<Set init...@>=
  514. schr[0]:='-';
  515. schr[1]:='.';
  516. schr[2]:='x';
  517. schr[3]:='*';
  518. schr[4]:=':';
  519. schr[5]:=dfl_chr;
  520. schr[6]:='+';
  521. schr[7]:='-';@/
  522. schr[@'10]:='+';
  523. schr[@'11]:='-';
  524. schr[@'12]:='x';
  525. schr[@'13]:='o';
  526. schr[@'14]:='o';
  527. schr[@'15]:='O';
  528. schr[@'16]:='o';
  529. schr[@'17]:='o';@/
  530. schr[@'20]:='=';
  531. schr[@'21]:='=';
  532. schr[@'22]:='<';
  533. schr[@'23]:='>';
  534. schr[@'24]:='<';
  535. schr[@'25]:='>';
  536. schr[@'26]:='<';
  537. schr[@'27]:='>';@/
  538. schr[@'30]:='~';
  539. schr[@'31]:='~';
  540. schr[@'32]:='<';
  541. schr[@'33]:='>';
  542. schr[@'34]:='<';
  543. schr[@'35]:='>';
  544. schr[@'36]:='<';
  545. schr[@'37]:='>';@/
  546. schr[@'40]:='-';
  547. schr[@'41]:='-';
  548. schr[@'42]:='|';
  549. schr[@'43]:='|';
  550. schr[@'44]:='-';
  551. schr[@'45]:='/';
  552. schr[@'46]:='\';
  553. schr[@'47]:='~';@/
  554. schr[@'50]:='-';
  555. schr[@'51]:='-';
  556. schr[@'52]:='|';
  557. schr[@'53]:='|';
  558. schr[@'54]:='-';
  559. schr[@'55]:='\';
  560. schr[@'56]:='/';
  561. schr[@'57]:=dfl_chr;@/
  562. schr[@'60]:='''';
  563. schr[@'61]:=dfl_chr;
  564. schr[@'62]:=dfl_chr;
  565. schr[@'63]:=dfl_chr;
  566. schr[@'64]:=dfl_chr;
  567. schr[@'65]:=dfl_chr;
  568. schr[@'66]:='/';
  569. schr[@'67]:=dfl_chr;@/
  570. schr[@'70]:=dfl_chr;
  571. schr[@'71]:=dfl_chr;
  572. schr[@'72]:='-';
  573. schr[@'73]:='0';
  574. schr[@'74]:='R';
  575. schr[@'75]:='I';
  576. schr[@'76]:='|';
  577. schr[@'77]:='|';@/
  578. schr[@'100]:=dfl_chr;
  579. schr[@'101]:='A';
  580. schr[@'102]:='B';
  581. schr[@'103]:='C';
  582. schr[@'104]:='D';
  583. schr[@'105]:='E';
  584. schr[@'106]:='F';
  585. schr[@'107]:='G';@/
  586. schr[@'110]:='H';
  587. schr[@'111]:='I';
  588. schr[@'112]:='J';
  589. schr[@'113]:='K';
  590. schr[@'114]:='L';
  591. schr[@'115]:='M';
  592. schr[@'116]:='N';
  593. schr[@'117]:='O';@/
  594. schr[@'120]:='P';
  595. schr[@'121]:='Q';
  596. schr[@'122]:='R';
  597. schr[@'123]:='S';
  598. schr[@'124]:='T';
  599. schr[@'125]:='U';
  600. schr[@'126]:='V';
  601. schr[@'127]:='W';@/
  602. schr[@'130]:='X';
  603. schr[@'131]:='Y';
  604. schr[@'132]:='Z';
  605. schr[@'133]:=dfl_chr;
  606. schr[@'134]:=dfl_chr;
  607. schr[@'135]:=dfl_chr;
  608. schr[@'136]:=dfl_chr;
  609. schr[@'137]:=dfl_chr;@/
  610. schr[@'140]:='-';
  611. schr[@'141]:='-';
  612. schr[@'142]:='|';
  613. schr[@'143]:='|';
  614. schr[@'144]:='|';
  615. schr[@'145]:='|';
  616. schr[@'146]:='{';
  617. schr[@'147]:='}';@/
  618. schr[@'150]:='<';
  619. schr[@'151]:='>';
  620. schr[@'152]:='|';
  621. schr[@'153]:='|';
  622. schr[@'154]:='|';
  623. schr[@'155]:='|';
  624. schr[@'156]:='\';
  625. schr[@'157]:=dfl_chr;@/
  626. schr[@'160]:='|';
  627. schr[@'161]:=dfl_chr;
  628. schr[@'162]:=dfl_chr;
  629. schr[@'163]:='S';
  630. schr[@'164]:=dfl_chr;
  631. schr[@'165]:=dfl_chr;
  632. schr[@'166]:='<';
  633. schr[@'167]:='>';@/
  634. for i:=@'170 to @'176 do schr[i]:=dfl_chr;
  635. for i:=@'177 to 255 do schr[i]:=dfl_chr;
  636. @ And then there is the math extension font which is used to create extended
  637. symbols.  We do the best that we can.
  638. @<Set init...@>=
  639. echr[0]:='(';
  640. echr[1]:=')';
  641. echr[2]:='[';
  642. echr[3]:=']';
  643. echr[4]:='|';
  644. echr[5]:='|';
  645. echr[6]:='|';
  646. echr[7]:='|';@/
  647. echr[@'10]:='{';
  648. echr[@'11]:='}';
  649. echr[@'12]:='<';
  650. echr[@'13]:='>';
  651. echr[@'14]:='|';
  652. echr[@'15]:='|';
  653. echr[@'16]:='/';
  654. echr[@'17]:='\';@/
  655. echr[@'20]:='(';
  656. echr[@'21]:=')';
  657. echr[@'22]:='(';
  658. echr[@'23]:=')';
  659. echr[@'24]:='[';
  660. echr[@'25]:=']';
  661. echr[@'26]:='|';
  662. echr[@'27]:='|';@/
  663. echr[@'30]:='|';
  664. echr[@'31]:='|';
  665. echr[@'32]:='{';
  666. echr[@'33]:='}';
  667. echr[@'34]:='<';
  668. echr[@'35]:='>';
  669. echr[@'36]:='/';
  670. echr[@'37]:='\';@/
  671. echr[@'40]:='(';
  672. echr[@'41]:=')';
  673. echr[@'42]:='[';
  674. echr[@'43]:=']';
  675. echr[@'44]:='|';
  676. echr[@'45]:='|';
  677. echr[@'46]:='|';
  678. echr[@'47]:='|';@/
  679. echr[@'50]:='{';
  680. echr[@'51]:='}';
  681. echr[@'52]:='<';
  682. echr[@'53]:='>';
  683. echr[@'54]:='/';
  684. echr[@'55]:='\';
  685. echr[@'56]:='/';
  686. echr[@'57]:='\';@/
  687. echr[@'60]:='(';
  688. echr[@'61]:=')';
  689. echr[@'62]:='|';
  690. echr[@'63]:='|';
  691. echr[@'64]:='|';
  692. echr[@'65]:='|';
  693. echr[@'66]:='|';
  694. echr[@'67]:='|';@/
  695. echr[@'70]:='(';
  696. echr[@'71]:=')';
  697. echr[@'72]:='(';
  698. echr[@'73]:=')';
  699. echr[@'74]:='{';
  700. echr[@'75]:='}';
  701. echr[@'76]:='|';
  702. echr[@'77]:='|';@/
  703. echr[@'100]:='(';
  704. echr[@'101]:=')';
  705. echr[@'102]:='|';
  706. echr[@'103]:='|';
  707. echr[@'104]:='<';
  708. echr[@'105]:='>';
  709. echr[@'106]:=dfl_chr;
  710. echr[@'107]:=dfl_chr;@/
  711. echr[@'110]:='S';
  712. echr[@'111]:='S';
  713. echr[@'112]:='o';
  714. echr[@'113]:='O';
  715. echr[@'114]:='+';
  716. echr[@'115]:='+';
  717. echr[@'116]:='x';
  718. echr[@'117]:='X';@/
  719. echr[@'120]:='E';
  720. echr[@'121]:=dfl_chr;
  721. echr[@'122]:='S';
  722. for i:=@'123 to @'127 do echr[i]:=dfl_chr;
  723. echr[@'130]:='E';
  724. echr[@'131]:=dfl_chr;
  725. echr[@'132]:='S';
  726. for i:=@'133 to @'141 do echr[i]:=dfl_chr;
  727. echr[@'142]:='^';
  728. echr[@'143]:='^';
  729. echr[@'144]:='^';
  730. echr[@'145]:='~';
  731. echr[@'146]:='~';
  732. echr[@'147]:='~';@/
  733. echr[@'150]:='[';
  734. echr[@'151]:=']';
  735. echr[@'152]:='|';
  736. echr[@'153]:='|';
  737. echr[@'154]:='|';
  738. echr[@'155]:='|';
  739. echr[@'156]:='{';
  740. echr[@'157]:='}';@/
  741. echr[@'160]:='|';
  742. echr[@'161]:='|';
  743. echr[@'162]:='|';
  744. echr[@'163]:='|';
  745. echr[@'164]:='|';
  746. echr[@'165]:='|';
  747. echr[@'166]:='|';
  748. echr[@'167]:='|';@/
  749. echr[@'170]:='|';
  750. echr[@'171]:='\';
  751. echr[@'172]:=dfl_chr;
  752. echr[@'173]:=dfl_chr;
  753. echr[@'174]:=dfl_chr;
  754. echr[@'175]:=dfl_chr;
  755. echr[@'176]:='|';
  756. echr[@'177]:='|';@/
  757. for i:=@'200 to 255 do echr[i]:=dfl_chr;
  758. @ The following system-independent code makes the |xord| array contain a
  759. suitable inverse to the information in |xchr|.
  760. @<Set init...@>=
  761. for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
  762. for i:=" " to "~" do xord[xchr[i]]:=i;
  763. @* Device-independent file format.
  764. Before we get into the details of \vutex, we need to know exactly
  765. what \.{DVI} files are. The form of such files was designed by David R.
  766. @^Fuchs, David Raymond@>
  767. Fuchs in 1979. Almost any reasonable typesetting device can be driven by
  768. a program that takes \.{DVI} files as input, and dozens of such
  769. \.{DVI}-to-whatever programs have been written. Thus, it is possible to
  770. print the output of document compilers like \TeX\ on many different kinds
  771. of equipment.
  772. A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
  773. series of commands in a machine-like language. The first byte of each command
  774. is the operation code, and this code is followed by zero or more bytes
  775. that provide parameters to the command. The parameters themselves may consist
  776. of several consecutive bytes; for example, the `|set_rule|' command has two
  777. parameters, each of which is four bytes long. Parameters are usually
  778. regarded as nonnegative integers; but four-byte-long parameters,
  779. and shorter parameters that denote distances, can be
  780. either positive or negative. Such parameters are given in two's complement
  781. notation. For example, a two-byte-long distance parameter has a value between
  782. $-2^{15}$ and $2^{15}-1$.
  783. @.DVI {\rm files}@>
  784. A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
  785. or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
  786. |pre| command, with its parameters that define the dimensions used in the
  787. file; this must come first.  Each ``page'' consists of a |bop| command,
  788. followed by any number of other commands that tell where characters are to
  789. be placed on a physical page, followed by an |eop| command. The pages
  790. appear in the order that they were generated, not in any particular
  791. numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
  792. (which are allowed between any two commands in the file), each |eop|
  793. command is immediately followed by a |bop| command, or by a |post|
  794. command; in the latter case, there are no more pages in the file, and the
  795. remaining bytes form the postamble.  Further details about the postamble
  796. will be explained later.
  797. Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
  798. quantities that give the location number of some other byte in the file;
  799. the first byte is number~0, then comes number~1, and so on. For example,
  800. one of the parameters of a |bop| command points to the previous |bop|;
  801. this makes it feasible to read the pages in backwards order, in case the
  802. results are being directed to a device that stacks its output face up.
  803. Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
  804. first page occupies bytes 100 to 999, say, and if the second
  805. page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
  806. points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
  807. very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)
  808. @ The \.{DVI} format is intended to be both compact and easily interpreted
  809. by a machine. Compactness is achieved by making most of the information
  810. implicit instead of explicit. When a \.{DVI}-reading program reads the
  811. commands for a page, it keeps track of several quantities: (a)~The current
  812. font |f| is an integer; this value is changed only
  813. by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
  814. is given by two numbers called the horizontal and vertical coordinates,
  815. |h| and |v|. Both coordinates are zero at the upper left corner of the page;
  816. moving to the right corresponds to increasing the horizontal coordinate, and
  817. moving down corresponds to increasing the vertical coordinate. Thus, the
  818. coordinates are essentially Cartesian, except that vertical directions are
  819. flipped; the Cartesian version of |(h,v)| would be |(h,-v)|.  (c)~The
  820. current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
  821. where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
  822. are used for vertical spacing. (d)~There is a stack containing
  823. |(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
  824. change the current level of operation. Note that the current font~|f| is
  825. not pushed and popped; the stack contains only information about
  826. positioning.
  827. The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
  828. to 32 bits, including the sign. Since they represent physical distances,
  829. there is a small unit of measurement such that increasing |h| by~1 means
  830. moving a certain tiny distance to the right. The actual unit of
  831. measurement is variable, as explained below.
  832. @ Here is a list of all the commands that may appear in a \.{DVI} file. Each
  833. command is specified by its symbolic name (e.g., |bop|), its opcode byte
  834. (e.g., 139), and its parameters (if any). The parameters are followed
  835. by a bracketed number telling how many bytes they occupy; for example,
  836. `|p[4]|' means that parameter |p| is four bytes long.
  837. \yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
  838. such that the reference point of the character is at |(h,v)|. Then
  839. increase |h| by the width of that character. Note that a character may
  840. have zero or negative width, so one cannot be sure that |h| will advance
  841. after this command; but |h| usually does increase.
  842. \yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
  843. Do the operations of |set_char_0|; but use the character whose number
  844. matches the opcode, instead of character~0.
  845. \yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
  846. number~|c| is typeset. \TeX82 uses this command for characters in the
  847. range |128<=c<256|.
  848. \yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
  849. bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
  850. command, which is intended for processors that deal with oriental languages;
  851. but \vutex\ will allow character codes greater than 255, assuming that
  852. they all have the same width as the character whose code is $c \bmod 256$.
  853. @^oriental characters@>@^Chinese characters@>@^Japanese characters@>
  854. \yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
  855. bytes long, so it can be as large as $2^{24}-1$.
  856. \yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
  857. bytes long, possibly even negative. Imagine that.
  858. \yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
  859. of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
  860. set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
  861. that if |b<0|, the value of |h| will decrease even though nothing else happens.
  862. Programs that typeset from \.{DVI} files should be careful to make the rules
  863. line up carefully with digitized characters, as explained in connection with
  864. the |rule_pixels| subroutine below.
  865. \yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
  866. such that the reference point of the character is at |(h,v)|. (The `put'
  867. commands are exactly like the `set' commands, except that they simply put out a
  868. character or a rule without moving the reference point afterwards.)
  869. \yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
  870. \yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
  871. \yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
  872. \yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
  873. |h| is not changed.
  874. \yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
  875. may occur between \.{DVI} commands, but a |nop| cannot be inserted between
  876. a command and its parameters or between two parameters.
  877. \yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
  878. of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
  879. the current font |f| to an undefined value.  The ten $c_i$ parameters can
  880. be used to identify pages, if a user wants to print only part of a \.{DVI}
  881. file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9}
  882. at the time \.{\\shipout} was invoked for this page.  The parameter |p|
  883. points to the previous |bop| command in the file, where the first |bop|
  884. has $p=-1$.
  885. \yskip\hang|eop| 140.  End of page: Print what you have read since the
  886. previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
  887. programs that drive most output devices will have kept a buffer of the
  888. material that appears on the page that has just ended. This material is
  889. largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
  890. |h|~coordinate; so it usually needs to be sorted into some order that is
  891. appropriate for the device in question. \vutex\ does not do such sorting.)
  892. \yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
  893. top of the stack; do not change any of these values. Note that |f| is
  894. not pushed.
  895. \yskip\hang|pop| 142. Pop the top six values off of the stack and assign
  896. them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
  897. of pushes, since it would be highly embarrassing if the stack were empty
  898. at the time of a |pop| command.
  899. \yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
  900. The parameter is a signed number in two's complement notation, |-128<=b<128|;
  901. if |b<0|, the reference point actually moves left.
  902. \yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
  903. two-byte quantity in the range |-32768<=b<32768|.
  904. \yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
  905. three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
  906. \yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
  907. four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
  908. \yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
  909. this parameterless command will usually suffice, because the same kind of motion
  910. will occur several times in succession; the following commands explain how
  911. |w| gets particular values.
  912. \yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
  913. signed quantity in two's complement notation, |-128<=b<128|. This command
  914. changes the current |w|~spacing and moves right by |b|.
  915. \yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
  916. parameter, |-32768<=b<32768|.
  917. \yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
  918. parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
  919. \yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
  920. parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
  921. \yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
  922. commands are like the `|w|' commands except that they involve |x| instead
  923. of |w|.
  924. \yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
  925. signed quantity in two's complement notation, |-128<=b<128|. This command
  926. changes the current |x|~spacing and moves right by |b|.
  927. \yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
  928. parameter, |-32768<=b<32768|.
  929. \yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
  930. parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
  931. \yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
  932. parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
  933. \yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
  934. The parameter is a signed number in two's complement notation, |-128<=a<128|;
  935. if |a<0|, the reference point actually moves up.
  936. \yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
  937. two-byte quantity in the range |-32768<=a<32768|.
  938. \yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
  939. three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
  940. \yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
  941. four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
  942. \yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
  943. this parameterless command will usually suffice, because the same kind of motion
  944. will occur several times in succession; the following commands explain how
  945. |y| gets particular values.
  946. \yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
  947. signed quantity in two's complement notation, |-128<=a<128|. This command
  948. changes the current |y|~spacing and moves down by |a|.
  949. \yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
  950. parameter, |-32768<=a<32768|.
  951. \yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
  952. parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
  953. \yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
  954. parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
  955. \yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
  956. are like the `|y|' commands except that they involve |z| instead of |y|.
  957. \yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
  958. signed quantity in two's complement notation, |-128<=a<128|. This command
  959. changes the current |z|~spacing and moves down by |a|.
  960. \yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
  961. parameter, |-32768<=a<32768|.
  962. \yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
  963. parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
  964. \yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
  965. parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
  966. \yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
  967. defined by a \\{fnt\_def} instruction, as explained below.
  968. \yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
  969. |f:=1|, \dots, |f:=63|, respectively.
  970. \yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
  971. numbers in the range |64<=k<256|.
  972. \yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
  973. bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
  974. command, but large font numbers may prove useful for specifications of
  975. color or texture, or they may be used for special fonts that have fixed
  976. numbers in some external coding scheme.
  977. \yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
  978. bytes long, so it can be as large as $2^{24}-1$.
  979. \yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
  980. bytes long; this is for the really big font numbers (and for the negative ones).
  981. \yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
  982. general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
  983. programs are being used. \TeX82 generates |xxx1| when a short enough
  984. \.{\\special} appears, setting |k| to the number of bytes being sent. It
  985. is recommended that |x| be a string having the form of a keyword followed
  986. by possible parameters relevant to that keyword.
  987. \yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
  988. \yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
  989. \yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
  990. large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.
  991. \yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  992. Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
  993. \yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  994. Define font |k|, where |0<=k<65536|.
  995. \yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  996. Define font |k|, where |0<=k<@t$2^{24}$@>|.
  997. \yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  998. Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
  999. \yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
  1000. Beginning of the preamble; this must come at the very beginning of the
  1001. file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
  1002. \yskip\hang|post| 248. Beginning of the postamble, see below.
  1003. \yskip\hang|post_post| 249. Ending of the postamble, see below.
  1004. \yskip\noindent Commands 250--255 are undefined at the present time.
  1005. @ @d set_char_0=0 {typeset character 0 and move right}
  1006. @d set1=128 {typeset a character and move right}
  1007. @d set_rule=132 {typeset a rule and move right}
  1008. @d put1=133 {typeset a character}
  1009. @d put_rule=137 {typeset a rule}
  1010. @d nop=138 {no operation}
  1011. @d bop=139 {beginning of page}
  1012. @d eop=140 {ending of page}
  1013. @d push=141 {save the current positions}
  1014. @d pop=142 {restore previous positions}
  1015. @d right1=143 {move right}
  1016. @d w0=147 {move right by |w|}
  1017. @d w1=148 {move right and set |w|}
  1018. @d x0=152 {move right by |x|}
  1019. @d x1=153 {move right and set |x|}
  1020. @d down1=157 {move down}
  1021. @d y0=161 {move down by |y|}
  1022. @d y1=162 {move down and set |y|}
  1023. @d z0=166 {move down by |z|}
  1024. @d z1=167 {move down and set |z|}
  1025. @d fnt_num_0=171 {set current font to 0}
  1026. @d fnt1=235 {set current font}
  1027. @d xxx1=239 {extension to \.{DVI} primitives}
  1028. @d xxx4=242 {potentially long extension to \.{DVI} primitives}
  1029. @d fnt_def1=243 {define the meaning of a font number}
  1030. @d pre=247 {preamble}
  1031. @d post=248 {postamble beginning}
  1032. @d post_post=249 {postamble ending}
  1033. @d undefined_commands==250,251,252,253,254,255
  1034. @ The preamble contains basic information about the file as a whole. As
  1035. stated above, there are six parameters:
  1036. $$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
  1037. The |i| byte identifies \.{DVI} format; currently this byte is always set
  1038. to~2. (Some day we will set |i=3|, when \.{DVI} format makes another
  1039. incompatible change---perhaps in 1992.)
  1040. The next two parameters, |num| and |den|, are positive integers that define
  1041. the units of measurement; they are the numerator and denominator of a
  1042. fraction by which all dimensions in the \.{DVI} file could be multiplied
  1043. in order to get lengths in units of $10^{-7}$ meters. (For example, there are
  1044. exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
  1045. points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000|
  1046. and $|den|=7227\cdot2^{16}=473628672$.)
  1047. @^sp@>
  1048. The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
  1049. desired magnification. The actual fraction by which dimensions are
  1050. multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document
  1051. does not call for any `\.{true}' dimensions, and if you change it only by
  1052. specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\
  1053. creates will be completely unchanged except for the value of |mag| in the
  1054. preamble and postable.
  1055. Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
  1056. interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
  1057. \vutex\ prints this comment out, which, in \TeX82 files, contains the date
  1058. and time of execution.
  1059. @d id_byte=2 {identifies the kind of \.{DVI} files described here}
  1060. @ Font definitions for a given font number |k| contain further parameters
  1061. $$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
  1062. The four-byte value |c| is the check sum that \TeX\ (or whatever program
  1063. generated the \.{DVI} file) found in the \.{TFM} file for this font;
  1064. |c| should match the check sum of the font found by programs that read
  1065. this \.{DVI} file.
  1066. @^check sum@>
  1067. Parameter |s| contains a fixed-point scale factor that is applied to the
  1068. character widths in font |k|; font dimensions in \.{TFM} files and other
  1069. font files are relative to this quantity, which is always positive and
  1070. less than $2^{27}$. It is given in the same units as the other dimensions
  1071. of the \.{DVI} file.  Parameter |d| is similar to |s|; it is the ``design
  1072. size,'' and it is given in \.{DVI} units that have not been corrected for
  1073. the magnification~|mag| found in the preamble.  Thus, font |k| is to be
  1074. used at $|mag|\cdot s/1000d$ times its normal size.
  1075. The remaining part of a font definition gives the external name of the font,
  1076. which is an ASCII string of length |a+l|. The number |a| is the length
  1077. of the ``area'' or directory, and |l| is the length of the font name itself;
  1078. the standard local system font area is supposed to be used when |a=0|.
  1079. The |n| field contains the area in its first |a| bytes.
  1080. Font definitions must appear before the first use of a particular font number.
  1081. Once font |k| is defined, it must not be defined again; however, we
  1082. shall see below that font definitions appear in the postamble as well as
  1083. in the pages, so in this sense each font number is defined exactly twice,
  1084. if at all. Like |nop| commands and \\{xxx} commands, font definitions can
  1085. appear before the first |bop|, or between an |eop| and a |bop|.
  1086. @ The last page in a \.{DVI} file is followed by `|post|'; this command
  1087. introduces the postamble, which summarizes important facts that \TeX\ has
  1088. accumulated about the file, making it possible to print subsets of the data
  1089. with reasonable efficiency.  \vutex\ does not make use of this capability,
  1090. however.
  1091. The postamble has the form
  1092. $$\vbox{\halign{\hbox{#\hfil}\cr
  1093.   |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
  1094.   $\langle\,$font definitions$\,\rangle$\cr
  1095.   |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
  1096. Here |p| is a pointer to the final |bop| in the file. The next three
  1097. parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
  1098. appeared in the preamble.
  1099. Parameters |l| and |u| give respectively the height-plus-depth of the tallest
  1100. page and the width of the widest page, in the same units as other dimensions
  1101. of the file. These numbers might be used by a \.{DVI}-reading program to
  1102. position individual ``pages'' on large sheets of film or paper.
  1103. Unfortunately, since characters can be set outside of the page boundaries,
  1104. these numbers cannot be used to set limits on the page area.
  1105. Parameter |s| is the maximum stack depth (i.e., the largest excess of
  1106. |push| commands over |pop| commands) needed to process this file. Then
  1107. comes |t|, the total number of pages (|bop| commands) present.
  1108. The postamble continues with font definitions, which are any number of
  1109. \\{fnt\_def} commands as described above, possibly interspersed with |nop|
  1110. commands. Each font number that is used in the \.{DVI} file must be defined
  1111. exactly twice: Once before it is first selected by a \\{fnt} command, and once
  1112. in the postamble.
  1113. @ The last part of the postamble, following the |post_post| byte that
  1114. signifies the end of the font definitions, contains |q|, a pointer to the
  1115. |post| command that started the postamble.  An identification byte, |i|,
  1116. comes next; this currently equals~2, as in the preamble.
  1117. The |i| byte is followed by four or more bytes that are all equal to
  1118. the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
  1119. these trailing bytes, until the total length of the file is a multiple of
  1120. four bytes, since this works out best on machines that pack four bytes per
  1121. word; but any number of 223's is allowed, as long as there are at least four
  1122. of them. In effect, 223 is a sort of signature that is added at the very end.
  1123. @^Fuchs, David Raymond@>
  1124. @* Plan of attack.
  1125. A \.{DVI} to device program must deal with a large amount of data.  The
  1126. \.{DVI} files in most applications tend to be quite large.
  1127. For this reason, one of the primary goals in the design of this program was
  1128. to reduce memory requirements as much as possible.  After much consideration
  1129. and examination of several other \.{DVI} drivers, it was decided that operation
  1130. of this program would occur in three distinct phases---prescan, font loading,
  1131. and page drawing.
  1132. Every \.{DVI} file is prescanned.  During this phase, information about font
  1133. usage is collected.  A very simple interpreter simply notices the |bop| and
  1134. |eop| tokens (to allow a subset of the pages to be printed), all of the
  1135. |font_def| commands (the name and other information is stored), and the
  1136. |set_char| commands (certain counters are incremented).  For every font that is
  1137. declared, space is allocated on the main |mem| array for a table of information
  1138. regarding that font.  This table contains that information that is necessary as
  1139. determined from the \TeX\ font metric (\.{TFM}) files.
  1140. During the second phase, the fonts are given a priority rating and the \.{TFM}
  1141. information table is created.  At this point, all the information about
  1142. font usage in the file is located in the |mem| array.  Therefore, a
  1143. routine named |base_font| is called.  This routine scans the declared fonts
  1144. and assesses them as either printable ASCII fonts or as unprintable fonts.
  1145. The printable fonts are given priority ratings based on the use of characters in
  1146. each font.  The font with priority 0 is the |base_font| and the horizontal
  1147. raster resolution is based on spacings determined by that font.
  1148. The vertical raster resolution is determined by the arbitrary assignment of a
  1149. set number of lines to the printed page.  Each character or word is then
  1150. assigned a line in the page according to its vertical coordinate.
  1151. The routine |load_tfm_file| creates a table for each font, declaring the width
  1152. of each character in \.{DVI} units.  Then a companion table is created with a
  1153. raster unit width for each character.
  1154. During phase three, the \.{DVI} file is reread one page at a time, and the
  1155. characters are ``drawn'' onto a |page|.  This is a character array which
  1156. will be output to the device when the page is completed.  Because of the coarse
  1157. raster resolution of the device, several characters may be placed within the
  1158. same array space, but the priority rating is used to determine which character
  1159. shall appear there.  Thus, a |priority| array is kept to determine the
  1160. priority of any character appearing on the |page|.  Also, because of
  1161. differences in \TeX\ font spacing, blank spaces may appear in words if the
  1162. characters were placed exactly in their raster positions.  Instead, a |word|
  1163. array positions a sequence of characters into adjacent raster positions and then
  1164. attempts to reset the sequence into the space allocated by \TeX\ in the
  1165. |page|.  Such words are left-justified in the allocated space so that
  1166. large spaces (or, possibly, one space) will appear between words in the
  1167. |page|.
  1168. We note that, if a sequence of characters is set from a font with smaller
  1169. character sizes than the |base_font|, then the space allocated by \TeX\ for that
  1170. word will be smaller than the raster width required.  Hence, words consisting of
  1171. characters from small fonts may be truncated.
  1172. \vutex\ output is in one of two modes --- compressed or pure \TeX.  In the pure
  1173. \TeX\ mode, each page is printed as it was constructed in the array, i.e. each
  1174. word is left-justified in the space provided by the \.{DVI} commands.  Again,
  1175. a number of spaces may appear between word, and the output can require a wide
  1176. palette.  Sub/super script lines will appear on separate associated lines.
  1177. The compression mode will shrink inter-word spacing to one space, with the first
  1178. character on each line appearing in its proper \TeX\ position, and all
  1179. subsequent words are shifted to the left.  Difficulties in associating sub/super
  1180. script lines with the proper word in the baseline has forced a vertical
  1181. compression of the associated lines into the baseline before the horizontal
  1182. compression takes place.  This may lead to ambiguous text, particularly with
  1183. mathematical expressions, but it is recommended that such text be viewed in the
  1184. unaltered non-compressed mode.
  1185. The format of the |mem| array reflects these phases.  It is organized as
  1186. a large array of 32-bit words.  The first 256 words are used as font pointers.
  1187. They are initialized to zero at the beginning of the program.  Then, whenever
  1188. font number |k| is defined, 262 words are allocated in the |mem| array for the
  1189. font, and |mem[k]| is set to point to the sixth word of the allocated space.
  1190. The first allocated word, or |mem[mem[k]-6]|, consists of two sixteen bit
  1191. integers.  The least significant sixteen bits are the external or directory
  1192. size of the font. The high order sixteen bits contain a pointer to the |names|
  1193. array, which contains the name of the font.
  1194. The next word (|mem[mem[k]-5]|) contains the |font_scaled_size| followed by the
  1195. font design size.  Then comes the |font_space|.  The next word is |font_type|
  1196. which determines the output array to use, followed by |font_status| to indicate
  1197. if the font is a printable font or not.
  1198. Finally comes the tabular information for the individual characters.  They each
  1199. use two words.  The first word, |mem[mem[k]+2*f]|, contains the |tfm_width| of
  1200. the character in \.{DVI} units.  The next contains the |use_count| for the first
  1201. pass, and the |raster_width| during the second pass.
  1202. During the first pass, the first word of the first character in the table
  1203. contains the checksum of the \.{TFM} file loaded from the \.{DVI} file.
  1204. Hopefully this will match the checksum in the \.{TFM} file.
  1205. The |names| array will contain all of the string information in the file.  It
  1206. is used as the input buffer from the terminal, the storage area for font names,
  1207. temporary space for special's, etc.  A `string' consists of a pointer into
  1208. this array.  The end of every string will be the value 0 as a marker.
  1209. |next_names_free| indicates the end of the table.
  1210. @ The following macroes should make the program slightly more readable, as they
  1211. calculate the offsets, etc. automatically.
  1212. @d font_desc_size=262
  1213. @d hi(#)==(# div 65536)
  1214. @d lo(#)==(# mod 65536)
  1215. @d raster_round(#)==round((#)/resol) + hh_offset
  1216. @d line_round(#)==round((#)/vresol) + vv_offset
  1217. @d set_line(#)==
  1218.    if line_for[#]<0 then
  1219.       begin line_for[#] := next_line_free;
  1220.       next_line_free:=next_line_free + page_width;
  1221.       end
  1222. @d @!font_check_sum == mem[cur_fptr] {Temporary location for checksum}
  1223. @d @!font_scaled_size == mem[cur_fptr-5] {Scaled size for current font}
  1224. @d @!font_design_size == mem[cur_fptr-4] {Design size for current font}
  1225. @d @!font_space == mem[cur_fptr-3] {Size of a thinspace in the font}
  1226. @d @!font_type == mem[cur_fptr-2] {Code to determine type of font}
  1227. @d @!declared == 1
  1228. @d @!other == 2
  1229. @d @!mexten == 3
  1230. @d @!msymbol == 4
  1231. @d @!mitalic == 5
  1232. @d @!tty == 6
  1233. @d @!roman == 7
  1234. @d @!font_status == mem[cur_fptr-1] {Priority of the current font}
  1235. @d @!font_nsave == mem[cur_fptr-6]  {For storing the name of the font}
  1236. @d @!font_name == hi(mem[cur_fptr-6]) {High sixteen bits for font name ptr}
  1237. @d @!directory_size == lo(mem[cur_fptr-6]) {Low sixteen bits for external
  1238.    size pointer}
  1239. @d @!tfm_width == mem[cur_char_ptr] {\.{TFM} width of current character}
  1240. @d @!use_count == mem[cur_char_ptr+1] {Use count used mostly in phase two.}
  1241. @d @!raster_width == mem[cur_char_ptr+1] {character width in rasters, phase
  1242.    three}
  1243. @d @!temp_ar(#) == mem[next_mem_free+#]  {`array' used for temporary storage}
  1244. @ We also need to define some of these variables, and initialize them.
  1245. @<Glob...@>=
  1246. @!mem : array [0..max_mem_size] of integer ; {major memory array}
  1247. @!names : array [0..name_size] of 0..127 ; {string array}
  1248. @!cur_fptr, @!cur_char_ptr : integer ; {temporary pointers}
  1249. @!next_mem_free, @!next_names_free : integer; { used for allocation }
  1250. i,j : integer ; {plain old index variable}
  1251. @ @<Set init...@>=
  1252. for i := 0 to 255 do
  1253.    mem[i] := 0 ; { no fonts defined yet.}
  1254. next_mem_free := 256 ; { next available position in mem array }
  1255. next_names_free := 1 ; { next available position in names array }
  1256. @* Input from binary files.
  1257. @<Types...@>=
  1258. @!eight_bits=0..255; {unsigned one-byte quantity}
  1259. @!byte_file=packed file of eight_bits; {files that contain binary data}
  1260. @!word_file=packed file of integer; {for pixel file words}
  1261. @ The program deals with two binary file variables: |dvi_file| is the main input
  1262. file that we are translating into symbolic form, and |tfm_file| is the current
  1263. \.{TFM} file from which character raster information is being read. The third
  1264. file is the final output file, a text file.
  1265. @<Glob...@>=
  1266. @!dvi_file:byte_file; {the stuff we are \.{DVI}typing}
  1267. @!tfm_file:byte_file; {a font metric file}
  1268. @!bit_file:text_file;  {where the final output goes}
  1269. @^system dependencies@>
  1270. @ To prepare these files for input, we |reset| them. An extension of
  1271. \PASCAL\ is needed in the case of |tfm_file|, since we want to associate
  1272. it with external files whose names are specified dynamically (i.e., not
  1273. known at compile time). The following code assumes that `|reset(f,s)|'
  1274. does this, when |f| is a file variable and |s| is a string variable that
  1275. specifies the file name. If |eof(f)| is true immediately after
  1276. |reset(f,s)| has acted, we assume that no file named |s| is accessible.
  1277. @^system dependencies@>
  1278. @p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
  1279. begin reset(dvi_file);
  1280. cur_loc:=0;
  1281. procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan}
  1282. begin reset(dvi_file);
  1283. cur_loc:=0;
  1284. procedure open_bit_file;  {prepares final output for writing}
  1285. begin rewrite(bit_file);
  1286. bit_is_open := true ;
  1287. procedure open_tfm_file; {opens \.{TFM} file}
  1288. begin reset(tfm_file,cur_name);
  1289. eof_tfm := eof(tfm_file);
  1290. procedure open_input_text ; {prepares to read from general purpose input}
  1291. begin reset(gen_input, cur_name) ;
  1292. end ;
  1293. @ |cur_loc| and |cur_name| are global variables: |cur_loc| is the number of the
  1294. byte about to be read next from |dvi_file|, and |cur_name| is a string variable
  1295. that will be set to the current \.{TFM} file name before |open_tfm_file| is
  1296. called.
  1297. @<Glob...@>=
  1298. @!cur_loc:integer; {where we are about to look, in |dvi_file|}
  1299. @!cur_name:packed array[1..name_length] of char; {external name,
  1300.   with no lower case letters}
  1301. @ We shall use another set of simple functions to read the next byte or
  1302. bytes from |dvi_file|. There are seven possibilities, each of which is
  1303. treated as a separate function in order to minimize the overhead for
  1304. subroutine calls.
  1305. @^system dependencies@>
  1306. @p function get_byte:integer; {returns the next byte, unsigned}
  1307. var b:eight_bits;
  1308. begin if eof(dvi_file) then get_byte:=0
  1309. else  begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
  1310.   end;
  1311. function signed_byte:integer; {returns the next byte, signed}
  1312. var b:eight_bits;
  1313. begin read(dvi_file,b); incr(cur_loc);
  1314. if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
  1315. function get_two_bytes:integer; {returns the next two bytes, unsigned}
  1316. var a,@!b:eight_bits;
  1317. begin read(dvi_file,a); read(dvi_file,b);
  1318. cur_loc:=cur_loc+2;
  1319. get_two_bytes:=a*256+b;
  1320. function signed_pair:integer; {returns the next two bytes, signed}
  1321. var a,@!b:eight_bits;
  1322. begin read(dvi_file,a); read(dvi_file,b);
  1323. cur_loc:=cur_loc+2;
  1324. if a<128 then signed_pair:=a*256+b
  1325. else signed_pair:=(a-256)*256+b;
  1326. function get_three_bytes:integer; {returns the next three bytes, unsigned}
  1327. var a,@!b,@!c:eight_bits;
  1328. begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
  1329. cur_loc:=cur_loc+3;
  1330. get_three_bytes:=(a*256+b)*256+c;
  1331. function signed_trio:integer; {returns the next three bytes, signed}
  1332. var a,@!b,@!c:eight_bits;
  1333. begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
  1334. cur_loc:=cur_loc+3;
  1335. if a<128 then signed_trio:=(a*256+b)*256+c
  1336. else signed_trio:=((a-256)*256+b)*256+c;
  1337. function signed_quad:integer; {returns the next four bytes, signed}
  1338. var a,@!b,@!c,@!d:eight_bits;
  1339. begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
  1340. cur_loc:=cur_loc+4;
  1341. if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
  1342. else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
  1343. @* Reading the font information.
  1344. \.{DVI} file format does not include information about character widths, since
  1345. that would tend to make the files a lot longer. But a program that reads a
  1346. \.{DVI} file is supposed to know the widths of the characters that appear in
  1347. \\{set\_char} commands.  Therefore \vutex\ reads in a \.{TFM} file for each
  1348. font that is used.
  1349. @ It is, of course, a simple matter to print the name of a given font.
  1350. @p procedure print_font(@!f:integer);
  1351. var k:0..name_size; {index into |names|}
  1352.    cur_fptr : integer ;
  1353. begin cur_fptr := mem[f]; if font_name=0 then print_ln('UNDEFINED!')
  1354. @.UNDEFINED@>
  1355. else  begin k := font_name ;
  1356.    if names[k]>0 then begin
  1357.       while names[k]>0 do begin
  1358.          print(xchr[names[k]]) ;
  1359.          k := k + 1 ;
  1360.       end ;
  1361.      print(':');
  1362.    end ;
  1363.    k := k + 1 ;
  1364.    while names[k]>0 do begin
  1365.       print(xchr[names[k]]);
  1366.       k := k + 1 ;
  1367.    end ;
  1368.   print_ln(' ');
  1369.  end;
  1370. @ We need a function that will read in a word from the \.{TFM} file.  If
  1371. the particular system
  1372. @^system dependencies@>
  1373. requires buffering, here is the place to do it.  It also sets a global flag
  1374. |eof_tfm| when it reaches the end of the file.  If this flag is set on
  1375. entrance to |load_tfm_file|, it is assumed that the file is bad.
  1376. @p function tfm_integer : integer ;
  1377. var i:integer;
  1378. begin read(tfm_file, i);
  1379. eof_tfm:=eof(tfm_file);
  1380. tfm_integer:=i;
  1381. @ There is nothing wrong with defining |eof_tfm| here.
  1382. @<Glob...@>=
  1383. @!eof_tfm:boolean;  {true when end of \.{TFM} file is reached.}
  1384. @ The most important part of |load_tfm_file| is the conversion to \.{DVI} units,
  1385. which involves multiplying the relative values in the \.{TFM} file by the
  1386. scaling factor in the \.{DVI} file. This fixed-point multiplication
  1387. must be done with precisely the same accuracy by all \.{DVI}-reading programs,
  1388. in order to validate the assumptions made by \.{DVI}-writing programs
  1389. like \TeX82.
  1390. Let us therefore summarize what needs to be done. Each width in a \.{TFM}
  1391. file appears as a four-byte quantity called a |fix_word|.  A |fix_word|
  1392. whose respective bytes are $(a,b,c,d)$ represents the number
  1393. $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
  1394. b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
  1395. -16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
  1396. (No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
  1397. dimension must be less than 16.)  We want to multiply this quantity by the
  1398. integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$.
  1399. If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
  1400. $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
  1401. 16, to obtain a multiplier less than $2^{23}$, and we can compensate for
  1402. this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
  1403. $\beta=2^{4-e}$; we shall compute
  1404. $$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
  1405. or the same quantity minus $\alpha$ if $a=255$.  This calculation must be
  1406. done exactly, for the reasons stated above; the following program does the
  1407. job in a system-independent way, assuming that arithmetic is exact on
  1408. numbers less than $2^{31}$ in magnitude.
  1409. @p function tfm_to_int(val:integer) : integer ;
  1410. var alpha, beta, x, z:integer ;  {help in converting widths}
  1411.     b0, b1, b2, b3:eight_bits ; {temporary byte storage}
  1412. begin z := font_scaled_size ;
  1413. @<Split tfm value up into bytes@>;
  1414. @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
  1415.   z:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta ;
  1416.   if b0=255 then z := z - alpha ;
  1417.   tfm_to_int := z ;
  1418. end ;
  1419. @ @<Split tfm value up...@>=
  1420. begin x:=val ;
  1421. if x>=0 then b0:=x div @'100000000
  1422. else begin x:=(x+@'10000000000)+@'10000000000; b0:=x div @'100000000+128;
  1423.      end ;
  1424. x:=x mod @'100000000;
  1425. b1:=x div @'200000;x:=x mod @'200000;b2:=x div @'400;b3:=x mod @'400; end;
  1426. @ @<Replace |z|...@>=
  1427. begin alpha:=16*z; beta:=16;
  1428. while z>=@'40000000 do
  1429.   begin z:=z div 2; beta:=beta div 2;
  1430.   end;
  1431. end ;
  1432. @ And, finally, we have the procedure which will extract the information from
  1433. the \.{TFM} file.  This routine is for input from the \.{TFM} file, which is (or
  1434. should be) a |packed file of integer|, so this should cause no grief.  See the
  1435. documentation on |tftopl| for the structure of the file.
  1436. @ @p procedure load_tfm_file ; {read in the width data}
  1437. label 9997, {used for bad format}
  1438.   9999; {used for normal exit}
  1439. var r:integer; {used for |cur_name| manipulation}
  1440.    i, k : integer ; {used for general pointer}
  1441.    bc, ec : integer ; {first and last characters in font}
  1442.    @!ptr : integer; {pointer to width table}
  1443. begin
  1444.    @<Move tfm name into the |cur_name| string@> ;
  1445.    if out_mode > 0 then print_ln('Trying to load ',cur_name) ;
  1446.    open_tfm_file ;
  1447.    k := next_mem_free ;
  1448.    if eof_tfm then goto 9997 ;
  1449.    while not eof_tfm do begin
  1450.       mem[k] := tfm_integer ;
  1451.       k := k + 1 ;
  1452.       if k > max_mem_size then
  1453.          abort(clone,' memory size exceeded on load of tfm file!') ;
  1454.    end ;
  1455.    bc := hi(mem[next_mem_free+1]) ;
  1456.    ec := lo(mem[next_mem_free+1]) ;
  1457.    if mem[next_mem_free+6]<>font_check_sum then
  1458.       print_ln('Checksum in tfm file does not match that in dvi file!') ;
  1459.    k := next_mem_free + lo(mem[next_mem_free]) + 6 - bc ;
  1460.    cur_char_ptr := cur_fptr ;
  1461.    for i := 0 to bc-1 do begin
  1462.       tfm_width := 0 ;
  1463.       cur_char_ptr := cur_char_ptr + 2 ;
  1464.    end ;
  1465.    for i := bc to ec do begin
  1466.       if mem[k+i] > 0 then
  1467.          ptr := mem[k+i] div @'100000000
  1468.       else
  1469.          ptr := ((mem[k+i] + one_fourth) + one_fourth) div @'100000000 + 128 ;
  1470.       tfm_width := tfm_to_int(mem[ptr + ec + k + 1 ]) ;
  1471.       cur_char_ptr := cur_char_ptr + 2 ;
  1472.    end ;
  1473.    for i := ec+1 to 127 do begin
  1474.       tfm_width := 0 ;
  1475.       cur_char_ptr := cur_char_ptr + 2 ;
  1476.    end ;
  1477.    goto 9999 ;
  1478. 9997: print_ln('---not loaded, TFM file is bad') ;
  1479. 9999: end ;
  1480. @* User interface.
  1481. \vutex\ is normally considered a filter, and therefore operates very
  1482. quietly unless some error is encountered.  However, for debugging purposes
  1483. or the inquisitive, one can access the variable |out_mode|.  This variable
  1484. will allow the tracing of \vutex.  It has two possible values:  0, and 1.
  1485. If it is zero, then the program is very quiet, only informing the user of
  1486. errors.  If it is one, a trace is executed for debugging purposes.
  1487. There are a few optional parameters requested when this program is invoked.  It
  1488. is possible to print only a restricted subset of the pages by specifying the
  1489. desired starting page and the maximum number of pages.  You also may select the
  1490. number of rasters across a page and the compression mode for output.
  1491. The starting page is specified by giving a sequence of 1 to 10 numbers or
  1492. asterisks separated by dots. For example, the specification `\.{1.*.-5}'
  1493. can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
  1494. and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
  1495. are followed by ten `count' values.) An asterisk matches any number,
  1496. so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
  1497. specifying the first page. If several pages match the given specification,
  1498. \vutex\ will begin with the earliest such page in the file. The
  1499. default specification `\.*' (which matches all pages) therefore denotes
  1500. the page at the beginning of the file.
  1501. When \vutex\ begins, it engages the user in a brief dialog so that the
  1502. options will be specified. This part of \vutex\ requires nonstandard \PASCAL\
  1503. constructions to handle the online interaction; so it may be preferable in some
  1504. cases to omit the dialog and simply to stick to the default options
  1505. (|out_mode=terse|, starting page `\.*', |max_pages=1000000|, |print_width=160|,
  1506. and |compress=false|).  On other hand, the system-dependent routines that are
  1507. needed are not complicated, so it will not be terribly difficult to introduce
  1508. them.  Normally, however, the necessary parameters should be taken from the
  1509. command line which invokes the program (if this is possible.)  Then, this part
  1510. of the program would not even be used.  This is in the domain of the change
  1511. file, however.
  1512. @^system dependencies@>
  1513. @d errors_only=0 {value of |out_mode| when minimal printing occurs}
  1514. @d terse=1 {value of |out_mode| for abbreviated output}
  1515. @<Glob...@>=
  1516. @!out_mode:errors_only..terse; {controls the amount of output}
  1517. @!max_pages:integer; {at most this many |bop..eop| pages will be printed}
  1518. @!page_width:integer; {page width for setting characters}
  1519. @!num_lines:integer; {number of lines in a page}
  1520. @!print_width: integer; {the number of raster units across screen before
  1521.         truncation}
  1522. @!print_height: integer; {the number of lines printed on each screen}
  1523. @!compress: boolean; {true if interword spacing to be compressed to one blank}
  1524. @!batch_mode: boolean; {true if pages to be processed with no interaction}
  1525. @ The starting page specification is recorded in two global arrays called
  1526. |start_count| and |start_there|. For example, `\.{1.*.-5}' is represented
  1527. by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
  1528. |start_there[2]=true|, |start_count[2]=-5|.
  1529. We also set |start_vals=2|, to indicate that count 2 was the last one
  1530. mentioned. The other values of |start_count| and |start_there| are not
  1531. important, in this example.
  1532. @<Glob...@>=
  1533. @!start_count:array[0..9] of integer; {count values to select starting page}
  1534. @!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
  1535. @!start_vals:0..9; {the last count considered significant}
  1536. @!count:array[0..9] of integer; {the count values on the current page}
  1537. @ @<Set init...@>=
  1538. out_mode := errors_only;
  1539. max_pages:=1000000; start_vals:=0; start_there[0]:=false;
  1540. page_width:= dfl_p_width; num_lines:=dfl_n_lines;
  1541. print_width:= dfl_screen_width; print_height:= dfl_screen_height;
  1542. compress:=false;batch_mode:=false;
  1543. @ Here is a simple subroutine that tests if the current page might be the
  1544. starting page.
  1545. @p function start_match:boolean; {does |count| match the starting spec?}
  1546. var k:0..9;  {loop index}
  1547. @!match:boolean; {does everything match so far?}
  1548. begin match:=true;
  1549. for k:=0 to start_vals do
  1550.   if start_there[k]and(start_count[k]<>count[k]) then match:=false;
  1551. start_match:=match;
  1552. @ The |input_ln| routine waits for the user to type a line at his or her
  1553. terminal; then it puts ASCII-code equivalents for the characters on that line
  1554. into the |buffer| array. The |term_in| file is used for terminal input,
  1555. and |term_out| for terminal output.
  1556. @^system dependencies@>
  1557. @<Glob...@>=
  1558. @!buffer:array[0..terminal_line_length] of ASCII_code;
  1559. @!term_in:text_file; {the terminal, considered as an input file}
  1560. @!term_out:text_file; {the terminal, considered as an output file}
  1561. @ Since the terminal is being used for both input and output, some systems
  1562. need a special routine to make sure that the user can see a prompt message
  1563. before waiting for input based on that message. (Otherwise the message
  1564. may just be sitting in a hidden buffer somewhere, and the user will have
  1565. no idea what the program is waiting for.) We shall call a system-dependent
  1566. subroutine |update_terminal| in order to avoid this problem.
  1567. @^system dependencies@>
  1568. @d update_terminal == break(term_out) {empty the terminal output buffer}
  1569. @ During the dialog, \vutex\ will treat the first blank space in a
  1570. line as the end of that line. Therefore |input_ln| makes sure that there
  1571. is always at least one blank space in |buffer|.
  1572. @^system dependencies@>
  1573. @p procedure input_ln; {inputs a line from the terminal}
  1574. var k:0..terminal_line_length;
  1575. begin update_terminal; reset(term_in);
  1576. if eoln(term_in) then read_ln(term_in);
  1577. k:=0;
  1578. while (k<terminal_line_length)and not eoln(term_in) do
  1579.   begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
  1580.   end;
  1581. buffer[k]:=" ";
  1582. @ The global variable |buf_ptr| is used while scanning each line of input;
  1583. it points to the first unread character in |buffer|.
  1584. @<Glob...@>=
  1585. @!buf_ptr:0..terminal_line_length; {the number of characters read}
  1586. @ Here is a routine that scans a (possibly signed) integer and computes
  1587. the decimal value. If no decimal integer starts at |buf_ptr|, the
  1588. value 0 is returned. The integer should be less than $2^{31}$ in
  1589. absolute value.
  1590. @p function get_integer : integer;
  1591. var x:integer; {accumulates the value}
  1592. @!negative:boolean; {should the value be negated?}
  1593. begin if buffer[buf_ptr]="-" then
  1594.   begin negative:=true; incr(buf_ptr);
  1595.   end
  1596. else negative:=false;
  1597. x:=0;
  1598. while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do
  1599.   begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr);
  1600.   end;
  1601. if negative then get_integer:=-x @+ else get_integer:=x;
  1602. @ The selected options are put into global variables by the |dialog|
  1603. procedure, which is called just as \vutex\ begins.
  1604. @^system dependencies@>
  1605. @p procedure dialog;
  1606. var k:integer; {loop variable}
  1607.     dum_width:integer;
  1608.     dum_height:integer;
  1609. begin rewrite(term_out); {prepare the terminal for output}
  1610. dum_width := 0; dum_height := 0;
  1611. @<Determine the desired interactive mode@>;
  1612. @<Determine the desired |start_count| values@>;
  1613. @<Determine the desired |max_pages|@>;
  1614. @<Determine the number of printed rasters |dum_width|@>;
  1615. @<Determine the number of printed rows |dum_height|@>;
  1616. @<Determine the compression mode@>;
  1617. @<Print all the selected options@>;
  1618. @<Print the page action options@>;
  1619. @<Adjust page dimensions@>;
  1620. @ @<Determine the desired interactive mode@>=
  1621. write(term_out,'Interactive mode (default=Y): ');
  1622. batch_mode := false; input_ln; buf_ptr:=0;
  1623. if (buffer[buf_ptr] = "N") or (buffer[buf_ptr] = "n") then batch_mode:=true ;
  1624. @ @<Determine the desired |start...@>=
  1625. write(term_out,'Starting page (default=*): ');
  1626. start_vals:=0; start_there[0]:=false;
  1627. input_ln; buf_ptr:=0; k:=0;
  1628. @<Parse the desired |start_vals|@>
  1629. @ These two routines have been split in half so it is easier to accept these
  1630. options from the command line if your operating system makes that possible.
  1631. Put a question mark at the end of the line to end it when doing it this way.
  1632. @<Parse the desired |start_vals|@>=
  1633. if (buffer[buf_ptr] <> "?") and (buffer[buf_ptr] <> " ") and
  1634.       (buffer[buf_ptr] <> "/") then begin
  1635.   repeat if buffer[buf_ptr]="*" then
  1636.     begin start_there[k]:=false; incr(buf_ptr);
  1637.     end
  1638.   else  begin start_there[k]:=true; start_count[k]:=get_integer;
  1639.     end;
  1640.   if (k<9)and(buffer[buf_ptr]=".") then
  1641.     begin incr(k); incr(buf_ptr);
  1642.     end
  1643.   else if (buffer[buf_ptr]=" ") or (buffer[buf_ptr]="/") or
  1644.          (buffer[buf_ptr]="?") then
  1645.      start_vals:=k
  1646.   else begin k := 0 ; start_vals := 0 ; end ;
  1647.   until start_vals=k ;
  1648. @ @<Determine the desired |max_pages|@>=
  1649. write(term_out,'Maximum number of pages (default=1000000): ');
  1650. max_pages := 1000000; input_ln; buf_ptr:=0;
  1651. @<Parse the desired |max_pages|@>
  1652. @ @<Parse the desired |max_pages|@>=
  1653.   begin max_pages:=get_integer;
  1654.   if max_pages=0 then
  1655.      max_pages := 1000000 ;
  1656.   end
  1657. @ @<Determine the number of printed rasters |dum_width|@>=
  1658. write(term_out,'Width of output in characters',
  1659.                ' (default=160/batch, 80/interactive): ');
  1660. input_ln; buf_ptr:=0; dum_width:=get_integer
  1661. @ @<Determine the number of printed rows |dum_height|@>=
  1662. write(term_out,'Number of printed lines per screen',
  1663.                ' (default=280/batch, 22/interactive): ');
  1664. input_ln; buf_ptr:=0;
  1665. dum_height:=get_integer
  1666. @ @<Determine the compression mode@>=
  1667. write(term_out,'Compress interword spaces, Y or N (default=N): ');
  1668. input_ln; buf_ptr:=0; compress:=false ;
  1669. if (buffer[buf_ptr] = "Y") or (buffer[buf_ptr] = "y") then compress:=true ;
  1670. @ We may have to adjust the page dimensions to fit on the internal array.  If
  1671. the compression mode has been chosen, we must allow extra room for the
  1672. uncompressed text.
  1673. @ @<Adjust page dimensions@>=
  1674. if dum_width=0 then
  1675.   if batch_mode then print_width := page_width
  1676.   else print_width := dfl_screen_width
  1677. else print_width := dum_width;
  1678. if compress and (print_width > (page_width * 3 div 4)) then
  1679.   page_width := 4 * print_width div 3
  1680. else if (not compress) and (print_width > page_width - hh_offset) then
  1681.   page_width := print_width + hh_offset;
  1682. if page_width > max_p_width then
  1683.   begin
  1684.     page_width := max_p_width;
  1685.     print_width := page_width - hh_offset;
  1686.   end;
  1687. num_lines := total_rast div page_width;
  1688. if dum_height=0 then
  1689.   if batch_mode then print_height := num_lines
  1690.   else print_height := dfl_screen_height
  1691. else print_height := dum_height;
  1692. if print_height > num_lines then print_height := num_lines
  1693. @ After the dialog is over, we print the options so that the user
  1694. can see what \vutex\ thought was specified.  This is only done if the
  1695. |out_mode| is greater than 0, so we can run quietly usually.
  1696. @<Print all the selected options@>=
  1697. if out_mode > 0 then begin
  1698. print_ln('Options selected:');
  1699. @.Options selected@>
  1700. print('  Starting page = ');
  1701. for k:=0 to start_vals do
  1702.   begin if start_there[k] then print(start_count[k]:1)
  1703.   else print('*');
  1704.   if k<start_vals then print('.')
  1705.   else print_ln(' ');
  1706.   end;
  1707. print_ln('  Maximum number of pages = ',max_pages:1);
  1708. print_ln('  Number of printed characters = ',print_width:1);
  1709. print_ln('  Number of printed lines = ',print_height:1);
  1710. @ If the user has selected interactive mode, then a summary of the the page
  1711. action options is printed for the user's information.
  1712. @<Print the page action options@>=
  1713. if not batch_mode then
  1714.   begin print_ln(' ');
  1715.   print_ln(' At the end of each printed screen, enter one of:');
  1716.   print_ln(' L - left, R - right, U - up, P - new page, <cr> - down');
  1717.   print_ln(' ');
  1718.   end;
  1719. @ Another procedure that is included for debugging and monitoring purposes
  1720. is the |diagnostics| procedure.  This procedure prints out the memory usage
  1721. of \vutex\.  Again, this is only done if the |out_mode| is greater than
  1722. zero.
  1723. @p procedure diagnostics ;
  1724. begin
  1725. if out_mode > 0 then begin
  1726. print_ln('Number of pages processed: ', actual_page_count:1) ;
  1727. print_ln('String pool usage: ',next_names_free:1, ' out of ', name_size:1,
  1728.       ' bytes') ;end ;
  1729. end ;
  1730. @* Defining fonts.
  1731. In \vutex, a font definition should only be recognized while the scanning
  1732. is not in the postamble, and when the pass is one.
  1733. A global variable |in_postamble| is provided to tell whether we are
  1734. processing the postamble or not.
  1735. @<Glob...@>=
  1736. @!in_postamble:boolean; {are we reading the postamble?}
  1737. @ @<Set init...@>=
  1738. in_postamble:=false;
  1739. @ The following subroutine does the necessary things when a \\{fnt\_def}
  1740. command is being processed.
  1741. @p procedure define_font(@!f:integer); {|f| is an external font number}
  1742. @!p:integer; {length of the area/directory spec}
  1743. @!n:integer; {length of the font name proper}
  1744. @!c,@!q,@!d:integer; {check sum, scaled size, and design size}
  1745. @!k:0..name_size; {indices into |names|}
  1746. begin if not in_postamble then begin
  1747.    if (f>255) then abort('Only font definitions 0..255 are valid in ',
  1748.                      clone) ;
  1749. @.vutex capacity exceeded...@>
  1750.    if (mem[f]<>0) then abort('Font number ',f:1,' already defined!') ;
  1751. @.Font already defined@>
  1752.    cur_fptr := next_mem_free+6 ;
  1753.    mem[f]:=cur_fptr ;
  1754.    next_mem_free := next_mem_free + font_desc_size ;
  1755. @<Read the font parameters into position for font |nf|, and
  1756.   print the font name@>;
  1757. end ;
  1758. end ;
  1759. @ This routine takes the data from the \.{DVI} file about the current font,
  1760. and loads it into memory.  It also initializes the |use_count| for each
  1761. character.
  1762. @<Read the font parameters into position for font |nf|...@>=
  1763. c:=signed_quad; font_check_sum:=c;@/
  1764. q:=signed_quad; font_scaled_size := q ;
  1765. d:=signed_quad; font_design_size := d ;
  1766. font_space:=d div 6;@/
  1767. p:=get_byte; n:=get_byte;
  1768. if next_names_free + p + n + 2 > name_size then
  1769.   abort(clone,' capacity exceeded (name size=',name_size:1,')!');
  1770. @.vutex capacity exceeded...@>
  1771. font_nsave := next_names_free * 65536 ;
  1772. if out_mode > 0 then print('Font ',f:1,': ');
  1773. for k := 1 to p do begin
  1774.    names[next_names_free] := get_byte ;
  1775.    next_names_free := next_names_free + 1 ;
  1776. end ;
  1777. names[next_names_free] := 0 ;
  1778. next_names_free := next_names_free + 1 ;
  1779. for k := 1 to n do begin
  1780.    names[next_names_free] := get_byte ;
  1781.    next_names_free := next_names_free + 1 ;
  1782. end ;
  1783. names[next_names_free] := 0 ;
  1784. next_names_free := next_names_free + 1 ;
  1785. cur_char_ptr := cur_fptr ;
  1786. for k := 0 to 127 do begin  {initialize use counts}
  1787.    use_count := 0 ;
  1788.    cur_char_ptr := cur_char_ptr + 2 ;
  1789. end ;
  1790. font_type := declared ;
  1791. if out_mode > 0 then print_font(f) ;
  1792. @ If |p=0|, i.e., if no font directory has been specified, \vutex\
  1793. is supposed to use the default font directory, which is a
  1794. system-dependent place where the standard font \.{TFM} files are kept.
  1795. @^system dependencies@>
  1796. @d tfm_directory_name=='TeXfonts:'
  1797. @d tfm_directory_name_length=9
  1798. @<Glob...@>=
  1799. @!tfm_directory:packed array[1..tfm_directory_name_length] of char;
  1800. @ We stick this initial value into the |names| array for later use.
  1801. @<Set init...@>=
  1802. tfm_directory:=tfm_directory_name;
  1803. @ The string |cur_name| is supposed to be set to the external name of the
  1804. \.{TFM} file for the current font.
  1805. @^system dependencies@>
  1806. @<Move tfm name into the |cur_name| string@>=
  1807. begin
  1808. for k:=1 to name_length do cur_name[k]:=' ';
  1809. r := 1 ;  k := font_name ;
  1810. if names[k]=0 then
  1811.   begin for i:=1 to tfm_directory_name_length do
  1812.     cur_name[i]:=tfm_directory[i];
  1813.   r:=tfm_directory_name_length+1;
  1814.   end  else begin
  1815.    while names[k] > 0 do begin
  1816.       cur_name[r] := xchr[names[k]] ;
  1817.       r := r + 1 ; k := k + 1 ;
  1818.    end ;
  1819.  end ;
  1820. k := k + 1 ;
  1821. while names[k] > 0 do begin
  1822.    cur_name[r] := xchr[names[k]] ;
  1823.    r := r + 1 ; k := k + 1 ;
  1824. end ;
  1825. cur_name[r] := '.' ;  r := r + 1 ;
  1826. cur_name[r] := 'T' ; r := r + 1 ;
  1827. cur_name[r] := 'F' ; r := r + 1 ;
  1828. cur_name[r] := 'M' ; r := r + 1 ;
  1829. for k := 1 to r do
  1830.    if (cur_name[k]>='a')and(cur_name[k]<='z') then
  1831.       cur_name[k]:=xchr[xord[cur_name[k]]-@'40] ;
  1832. end ;
  1833. @* Special execution.
  1834. In this implementation, special commands are ignored.  However, the structure to
  1835. handle such remains as a skeleton if some special procedures are desired.
  1836. We need a procedure that will handle any special commands that might appear.
  1837. Commands must have the following format:
  1838. \medskip
  1839. \line{\hskip20pt command(parameters$\ldots$)\hfil}
  1840. \medskip
  1841. If there is more than one command in a special, they must be separated by
  1842. spaces.  The parameters are also separated by spaces.  The string that was
  1843. contained in the special command is in the names array starting at
  1844. |next_names_free| and ending with a 0.  Also, the |prescan| boolean should be
  1845. checked to see if this is the first or second time that this string was
  1846. encountered.
  1847. @p procedure do_special ;
  1848. begin
  1849. end ;
  1850. @ This is where the special commands are interpreted.  We accumulate the
  1851. string, and pass a pointer to it to a routine called |do_special|.  Note that
  1852. this routine is called twice, once for the prescan, and once for the actual
  1853. processing.  This way, special commands that set up the device for a particular
  1854. mode can be included and processed during the second pass.  During pass one,
  1855. specials on pages before the first page to be processed are also passed, as
  1856. they might contain configuration information.
  1857. @<Translate an |xxx| command@>=
  1858. begin
  1859. bad_char:=false;
  1860. if p+next_names_free > name_size then
  1861.    abort('Out of string space during special!') ;
  1862. @.Out of string space during special@>
  1863. for k:=1 to p do
  1864.   begin q:=get_byte;
  1865.   if (q<" ")or(q>"~") then
  1866.      bad_char:=true;
  1867.   end;
  1868. if bad_char then print_ln('non-ASCII character in xxx command!');
  1869. @.non-ASCII character...@>
  1870. do_special ;
  1871. end ;
  1872. @* Interpreting the page commands.
  1873. The main work of \vutex\ is accomplished by the |do_page| procedure,
  1874. which produces the output for an entire page, assuming that the |bop|
  1875. command for that page has already been processed. This procedure is
  1876. essentially an interpretive routine that reads and acts on the \.{DVI}
  1877. commands.  Of course, this is for the second pass.  A |prescan| routine
  1878. is still necessary to load the font information.
  1879. @ The definition of \.{DVI} files refers to six registers,
  1880. $(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.  In practice,
  1881. we also need registers |hh| and |vv|, the raster analogs of $h$ and $v$,
  1882. since it is not always true that |hh=raster_round(h)| or
  1883. |vv=line_round(v)|.
  1884. The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
  1885. called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.
  1886. @<Glob...@>=
  1887. @!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
  1888. @!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
  1889.   array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
  1890. @!hhstack,@!vvstack:
  1891.   array [0..stack_size] of integer; {pushed down values in rasters}
  1892. @ Three characteristics of the pages (their |max_v|, |max_h|, and
  1893. |max_s|) are specified in the postamble, and a warning message
  1894. is printed if these limits are exceeded. Actually |max_v| is set to
  1895. the maximum height plus depth of a page, and |max_h| to the maximum width,
  1896. for purposes of page layout. Since characters can legally be set outside
  1897. of the page boundaries, it is not an error when |max_v| or |max_h| is
  1898. exceeded. But |max_s| should not be exceeded.
  1899. The postamble also specifies the total number of pages; \vutex\
  1900. checks to see if this total is accurate.
  1901. @<Glob...@>=
  1902. @!max_v:integer; {the value of |abs(v)| should probably not exceed this}
  1903. @!max_h:integer; {the value of |abs(h)| should probably not exceed this}
  1904. @!max_s:integer; {the stack depth should not exceed this}
  1905. @!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
  1906. @!page_count:integer; {the total number of pages seen so far}
  1907. @!hpos_ed, @!vpos_ed : boolean ; {are we positioned in this respect?}
  1908. @ @<Set init...@>=
  1909. max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
  1910. max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
  1911. @ Before we get into the details of |do_page|, it is convenient to
  1912. consider a simpler routine that computes the first parameter of each
  1913. opcode.
  1914. @d four_cases(#)==#,#+1,#+2,#+3
  1915. @d eight_cases(#)==four_cases(#),four_cases(#+4)
  1916. @d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
  1917. @d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
  1918. @d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
  1919. @p function first_par(o:eight_bits):integer;
  1920. begin case o of
  1921. sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
  1922.   abort('Can''t call first_par like this.') ;
  1923. set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
  1924. set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
  1925. set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
  1926. right1,w1,x1,down1,y1,z1: first_par:=signed_byte;
  1927. right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
  1928. right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
  1929. set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
  1930.   fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
  1931. nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
  1932. w0: first_par:=w;
  1933. x0: first_par:=x;
  1934. y0: first_par:=y;
  1935. z0: first_par:=z;
  1936. sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
  1937. @ We need a routine to skip over a font definition.  This is very simple,
  1938. just requires skipping over the parameters:
  1939. @p procedure skip_font_def ;
  1940. var @!dumq, @!dumi, i : integer ;
  1941. begin
  1942.    dumq := signed_quad ; dumq := signed_quad ; dumq := signed_quad ;
  1943.    dumi := get_byte + get_byte ;
  1944.    for i := 1 to dumi do dumi := get_byte ;
  1945. end ;
  1946. @ A useful constant:
  1947. @<Constants...@>=
  1948. one_fourth = 1073741824 ;
  1949. @ Strictly speaking, the |do_page| procedure is really a function with
  1950. side effects, not a `\&{procedure}'; it returns the value |false| if
  1951. \vutex\ should be aborted because of some unusual happening. The
  1952. subroutine is organized as a typical interpreter, with a multiway branch
  1953. on the command code followed by |goto| statements leading to routines that
  1954. finish up the activities common to different commands. We will use the
  1955. following labels:
  1956. @d fin_set=41 {label for commands that set or put a character}
  1957. @d fin_rule=42 {label for commands that set or put a rule}
  1958. @d move_right=43 {label for commands that change |h|}
  1959. @d move_down=44 {label for commands that change |v|}
  1960. @d show_state=45 {label for commands that change |s|}
  1961. @d change_font=46 {label for commands that change |cur_font|}
  1962. @ Now we have the actual routine that draws a character.  It simply sets the
  1963. character pointer and calls the routine to set the character in the page.
  1964. @p procedure draw_char(p:integer) ;
  1965. @<Character drawing procedures@>
  1966. begin cur_char_ptr := cur_fptr + 2*p ;
  1967.    dev_char_draw ;
  1968.    hpos_ed := false ;
  1969. end ;
  1970. @ Simple horizontal or vertical rules can be created from underscores and
  1971. vertical bars.  They are given low priority and will be replaced by all other
  1972. characters. If a vertical line segment is the only character to appear on a line
  1973. in the page, then that line will not be printed.
  1974. @p procedure dev_rule_draw(rh, rw: integer) ;
  1975. label done ;
  1976. var position : integer ;
  1977. i,j : integer ;
  1978. begin if line_for[vv] < 0 then begin
  1979.   line_for[vv] := next_line_free ;
  1980.   next_line_free := next_line_free + page_width ;
  1981.   end;
  1982.   position := line_for[vv] ; j := position + hh + 1 ;
  1983.    if j < position + page_width then
  1984.       if rw >= rh then
  1985.         begin for i := 1 to rw do
  1986.           begin if (page[j] = xchr[32]) and (j > position) then
  1987.               page[j] := xchr[95]
  1988.             else if page[position] = xchr[32] then page[position] := xchr[62] ;
  1989.             incr(j) ;
  1990.             if (j >= position + page_width) then
  1991.               begin if page[position] = xchr[32] then
  1992.                               page[position] := xchr[62] ;
  1993.                 goto done ;
  1994.               end
  1995.           end ;
  1996.       end else if j > position then
  1997.         begin for i := 1 to rh do
  1998.           begin if page[j] = xchr[32] then
  1999.               page[j] := xchr[124]
  2000.             else page[j-hh-1] := xchr[62] ;
  2001.             if line_for[vv-i] < 0 then
  2002.               begin line_for[vv-i] := next_line_free ;
  2003.                 next_line_free := next_line_free + page_width ;
  2004.               end ;
  2005.             position := line_for[vv-i] ;
  2006.             j := position + hh + 1 ;
  2007.             if j < 0 then goto done ;
  2008.           end ;
  2009.         end ;
  2010. done: end ;
  2011. @ On entrance to this routine, the character to be drawn will be in |p|, the
  2012. font in |cur_fptr|.  The routine should be responsible for insuring that the
  2013. device is set to the correct location on the page (|hh| and |vv|) and that the
  2014. current font is selected.  The characters are drawn sequentially into a word
  2015. array.
  2016. @<Character drawing procedures@>=
  2017. procedure dev_char_draw ;
  2018. var position : integer ; {points to beginning of raster row}
  2019. i : integer ;
  2020. c : char ; {the ASCII character to be set}
  2021. begin if not set_word then @<Initialize word@> ;
  2022. incr(in_word) ; position := line_for[word_vv] ;
  2023. hpos_ed := true ; vpos_ed := true ;
  2024. if (hh >= 0 ) then
  2025.    if (word_hh+in_word < page_width) then
  2026.       begin i := position+ word_hh + in_word ;
  2027.         if (i < total_rast) then
  2028.           if (ord(priority[i]) > font_status) and
  2029.                   (ord(word_priority[in_word]) > font_status) then
  2030.             begin case font_type of
  2031.                roman: @<Cases for the roman fonts@> ;
  2032.                tty: c := xchr[p];
  2033.                mitalic: c := mchr[p];
  2034.                msymbol: c := schr[p];
  2035.                mexten: c := echr[p];
  2036.                othercases c := dfl_chr
  2037.                endcases ;
  2038.                word[in_word] := c ;
  2039.                word_priority[in_word] := xchr[font_status] ;
  2040.             end
  2041.           else if page[position] = xchr[32] then
  2042.             page[position] := xchr[62]
  2043.         else if page[position] = xchr[32] then
  2044.           page[position] := xchr[62] ;
  2045.      end else if page[position] = xchr[32] then
  2046.          page[position] := xchr[62]
  2047. else if page[position] = xchr[32] then
  2048.   page[position] := xchr[62] ;
  2049. if font_status > 0 then
  2050.    if (page[position] = xchr[32]) or (page[position]=xchr[62]) then
  2051.       page[position] := xchr[42];
  2052. end ;
  2053. @ Variables must be initialized at the beginning of a new word.  If the word is
  2054. appearing on a new line, the first space in the priority array is used to flag
  2055. the line as a baseline or an associated sub/super script line.
  2056. @<Initialize word@>=
  2057. begin set_word := true ;
  2058. word_hh := hh; word_vv := vv ;
  2059. in_word := 0;
  2060. if line_for[vv] < 0 then
  2061.   begin line_for[vv] := next_line_free ;
  2062.   next_line_free := next_line_free + page_width ;
  2063.   end ;
  2064. if prev_vv >= 0 then
  2065.   begin if (priority[line_for[vv]]=xchr[126]) then
  2066.     if prev_vv=vv-1 then
  2067.       begin page[line_for[vv]] := '-' ;
  2068.       priority[line_for[vv]] := '-' ;
  2069.       end
  2070.     else if prev_vv=vv+1 then
  2071.       begin page[line_for[vv]] := '+' ;
  2072.       priority[line_for[vv]] := '+' ;
  2073.       end
  2074.     else priority[line_for[vv]] := 'b'
  2075.   end
  2076. else priority[line_for[vv]] := 'b' ;
  2077. prev_vv := vv ;
  2078. for i := 1 to page_width do
  2079.   begin word[i] := ' ' ;
  2080.   word_priority[i] := xchr[126] ;
  2081.   end ;
  2082. @ The layout for the standard roman fonts is slightly different from the ASCII
  2083. table.
  2084. @d ff == @'13  {these define the ligatures in roman fonts}
  2085. @d fi == @'14
  2086. @d fl == @'15
  2087. @d ffi == @'16
  2088. @d ffl == @'17
  2089. @d ss == @'31
  2090. @d ae == @'32
  2091. @d oe == @'33
  2092. @d AE == @'35
  2093. @d OE == @'36
  2094. @<Cases for the roman fonts@>=
  2095. case p of
  2096.   ff: begin word[in_word] := rchr[102] ;
  2097.      word_priority[in_word] := xchr[font_status] ;
  2098.      incr(in_word) ; c := rchr[102] ; end ;
  2099.  fi: begin word[in_word] := rchr[102] ;
  2100.      word_priority[in_word] := xchr[font_status] ;
  2101.      incr(in_word) ; c := rchr[105] ; end ;
  2102.  fl: begin word[in_word] := rchr[102] ;
  2103.      word_priority[in_word] := xchr[font_status] ;
  2104.      incr(in_word) ; c := rchr[108] ; end ;
  2105.  ffi: begin word[in_word] := rchr[102] ;
  2106.       word_priority[in_word] := xchr[font_status] ;
  2107.       incr(in_word) ; word[in_word] := rchr[102] ;
  2108.       word_priority[in_word] := xchr[font_status] ;
  2109.       incr(in_word) ; c := rchr[105] ; end ;
  2110.  ffl: begin word[in_word] := rchr[102] ;
  2111.       word_priority[in_word] := xchr[font_status] ;
  2112.       incr(in_word) ; word[in_word] := rchr[102] ;
  2113.       word_priority[in_word] := xchr[font_status] ;
  2114.       incr(in_word) ; c := rchr[108] ; end ;
  2115.  ss: begin word[in_word] := rchr[115] ;
  2116.      word_priority[in_word] := xchr[font_status] ;
  2117.      incr(in_word) ; c := rchr[115] ; end ;
  2118.  ae: begin word[in_word] := rchr[97] ;
  2119.      word_priority[in_word] := xchr[font_status] ;
  2120.      incr(in_word) ; c := rchr[101] ; end ;
  2121.  oe: begin word[in_word] := rchr[111] ;
  2122.      word_priority[in_word] := xchr[font_status] ;
  2123.      incr(in_word) ; c := rchr[101] ; end ;
  2124.  AE: begin word[in_word] := rchr[65] ;
  2125.      word_priority[in_word] := xchr[font_status] ;
  2126.      incr(in_word) ; c := rchr[69] ; end ;
  2127.  OE: begin word[in_word] := rchr[79] ;
  2128.      word_priority[in_word] := xchr[font_status] ;
  2129.      incr(in_word) ; c := rchr[69] ; end ;
  2130.  othercases c:= rchr[p]
  2131.  endcases
  2132. @ We need the global page arrays
  2133. @<Glob...@>=
  2134. @!page : array [0..total_rast] of text_char ; {the page of data}
  2135. @!priority : array [0..total_rast] of text_char ; {priority array}
  2136. @!line_for : array [1..dfl_n_lines] of integer ; {points to line in array}
  2137. @!next_line_free : integer ; {points to the next free position in array}
  2138. @!word : array [0..max_p_width] of text_char ; {string to hold complete word}
  2139. @!word_priority : array [0..max_p_width] of text_char ; {priority of each char}
  2140. @!in_word : integer ; {points to location in word}
  2141. @!set_word : boolean ; {set to true if a word has begun}
  2142. @!word_hh : integer ; {hh value where word began}
  2143. @!word_vv : integer ; {vv value of first character in word}
  2144. @!min_hh,@!min_vv : integer ; {minimum hh, vv values on the page}
  2145. @!prev_vv: integer ;
  2146. @ This routine will reset the characters in a word if the positioning has
  2147. left holes or missing characters.
  2148. @p procedure reset_word ;
  2149. var last:integer; {position of last character in word to be reset}
  2150. i,j,position : integer ;
  2151. begin set_line(vv) ;
  2152. if word_hh < min_hh then min_hh := max(0,word_hh) ;
  2153. if word_vv < min_vv then min_vv := max(0,word_vv) ;
  2154. if hh > word_hh then
  2155.    begin position := line_for[word_vv] ;
  2156.       j := position + word_hh + 1 ;
  2157.       last := hh - word_hh + 1 ; if last > in_word then last := in_word ;
  2158.       for i := 1 to last do
  2159.          begin if j > position then
  2160.             if priority[j] > word_priority[i] then
  2161.                begin page[j] := word[i] ;
  2162.                   priority[j] := word_priority[i] ;
  2163.                end
  2164.             else if page[position] = xchr[32] then
  2165.               page[position] := xchr[62]
  2166.          else if page[position] = xchr[32] then
  2167.            page[position] := xchr[62] ;
  2168.          incr(j) ;
  2169.       end ;
  2170.    end ;
  2171.    set_word := false ;
  2172. end ;
  2173. @ This routine is called at the beginning of each page, before anything
  2174. is processed.
  2175. @p  procedure start_page ;
  2176. var i : integer ;
  2177. begin
  2178.    hpos_ed := false ; vpos_ed := false ;
  2179.    for i := 0 to total_rast do
  2180.       begin page[i] := xchr[32];
  2181.       priority[i] := xchr[126] ;
  2182.    end;
  2183.    for i := 1 to num_lines do line_for[i] := -1;
  2184.    next_line_free := 0;
  2185.    min_hh :=hh_offset ; min_vv := vv_offset ;
  2186.    prev_vv := -1 ;
  2187. end ;
  2188. @ These constants define the key strokes for interactive paging action.
  2189. @<Constants...@>=
  2190. @!u_left = "L";  l_left="l";   {key for move left on page}
  2191. @!u_right= "R";  l_right="r";  {key for move right on page}
  2192. @!u_up= "U";     l_up="u";     {key for move up on page}
  2193. @!u_page= "P";   l_page="p";   {key for move to next page}
  2194. @!u_comp= "C";   l_comp="c";   {key for compress text on page}
  2195. @ When this routine is called, a full page has been processed, and the
  2196. page array is sent to the output device.
  2197. @p procedure finish_page ;
  2198. label done;
  2199. var i,j,k: integer ; {index counters}
  2200.     last_hh: integer ; {raster column of furthest right character}
  2201.     last_vv: integer ; {raster row of furthest down character}
  2202.     first_p_hh: integer ; {raster column of first printed character}
  2203.     last_p_hh: integer ; {raster column of last printed character}
  2204.     first_p_row: integer ; {row number of first printed row}
  2205.     last_p_row: integer ; {row number of last printed row}
  2206.     rows_printed: integer ;
  2207.     raster: integer ;
  2208.     jspace: integer ;
  2209.     shift: integer ;
  2210.     next_row: integer ;
  2211.     position: integer ;
  2212.     average: integer;  {average spacing between baselines}
  2213.     menu : integer ; {used to interactively select page-action}
  2214.     printing_page : boolean ;
  2215. begin first_p_hh := min_hh ; first_p_row := min_vv ;
  2216. last_hh := raster_round(max_h_so_far) + 1 ;
  2217. last_vv := line_round(max_v_so_far) ;
  2218. if last_hh >= page_width then
  2219.    last_hh := page_width - 1 ;
  2220. @<Determine average spacing between nonempty lines@> ;
  2221. if compress then @<Compress spaces between words@> ;
  2222. printing_page := true;
  2223. position := first_p_row;
  2224. while (printing_page) do
  2225.    begin last_p_hh := first_p_hh + print_width-2;
  2226.    if last_p_hh >= page_width then last_p_hh := page_width;
  2227.    write_ln(bit_file);
  2228.    i := first_p_row;
  2229.    rows_printed := 0;
  2230.    while (i <= last_vv) and (rows_printed < print_height) do
  2231.       begin if line_for[i] >= 0 then
  2232.          begin position := i ;
  2233.          raster := line_for[i] ;
  2234.          if raster > total_rast-last_hh-1 then goto done ;
  2235.          write(bit_file,page[raster]) ;
  2236.          k := raster + last_p_hh ;
  2237.          while page[k] = xchr[32] do decr(k) ;
  2238.          k := k - raster - 1 ;
  2239.          raster := raster + first_p_hh ;
  2240.          for j := first_p_hh to k do
  2241.             begin write(bit_file,page[raster]) ;
  2242.             incr(raster) ;
  2243.             end ;
  2244.          write_ln(bit_file,page[raster]) ;
  2245.          incr(rows_printed) ;
  2246.          end
  2247.          else if (average >0) and (i-position > average) then
  2248.            begin write_ln(bit_file) ;
  2249.            position := i ; incr(rows_printed) ;
  2250.            end ;
  2251.       incr(i);
  2252.       end;
  2253.    done: @<Determine page action@> ;
  2254.    end;
  2255. end ;
  2256. @ Since each physical line is represented by perhaps several lines in the page
  2257. arrays, we try to determine the spacing between physical lines by taking the
  2258. average number of blank lines in the array between nonempty lines.  This is used
  2259. to approximate the number of blank lines in a vertical shift in \TeX.
  2260. Note that a page number at the bottom of a sparse page may distort the average.
  2261. Hence if the last line is set apart substantially, we will ignore it.
  2262. @<Determine average spacing between nonempty lines@>=
  2263. begin
  2264.   average := 0 ; k:= 0 ;
  2265.   i := 1 ; j := line_for[i] ;
  2266.   while (i <= last_vv) and ((j < 0) or (priority[j] <>'b')) do
  2267.     begin incr(i); j:= line_for[i];
  2268.     end;
  2269.   position := i ;
  2270.   incr(i) ;
  2271.   while i <= last_vv do
  2272.     begin j := line_for[i] ;
  2273.     if (j>=0) then
  2274.       if (priority[j]='b') then
  2275.         begin shift := i - position ;
  2276.         average := average + shift ;
  2277.         incr(k) ; position := i ;
  2278.         end;
  2279.     incr(i);
  2280.     end ;
  2281.   if k > 1 then
  2282.     begin if shift > 1.1*round(average*1.0/k) then
  2283.       begin k := k - 1 ;
  2284.       average := average - shift ;
  2285.       end;
  2286.     if k > 1 then
  2287.       average := round(average*1.0 / k )
  2288.     end;
  2289. @ If the compress option is chosen, the text is compressed vertically so that
  2290. associated sub/super script lines are pushed into the baseline.  Then the
  2291. horizontal spacing is abandonned and interword spacing is compressed to one
  2292. blank space.
  2293. @<Compress spaces between words@>=
  2294. begin
  2295. for i := min_vv to last_vv do
  2296.   if line_for[i]>=0 then
  2297.     if priority[line_for[i]]='+' then begin
  2298.        raster := line_for[i] ;
  2299.        position := line_for[i+1] ;
  2300.        for j := min_hh to last_hh do
  2301.          if (page[raster+j]<>xchr[32]) then
  2302.            if (page[position+j]=xchr[32]) or
  2303.                       (priority[raster+j]<priority[position+j]) then
  2304.              begin
  2305.                page[position+j] := page[raster+j] ;
  2306.                priority[position+j] := priority[raster+j];
  2307.              end;
  2308.        line_for[i] := -2
  2309.        end ;
  2310. for i := last_vv downto min_vv do
  2311.   if line_for[i]>=0 then
  2312.     if priority[line_for[i]]='-' then begin
  2313.        raster := line_for[i] ;
  2314.        position := line_for[i-1] ;
  2315.        for j := min_hh to last_hh do
  2316.          if (page[raster+j]<>xchr[32]) then
  2317.            if (page[position+j]=xchr[32]) or
  2318.                       (priority[raster+j]<priority[position+j]) then
  2319.              begin
  2320.                page[position+j] := page[raster+j] ;
  2321.                priority[position+j] := priority[raster+j];
  2322.              end;
  2323.        line_for[i] := -2
  2324.        end ;
  2325. for i := min_vv to last_vv do
  2326.   if line_for[i]>=0 then
  2327.     begin raster := line_for[i] + 1 ;
  2328.     next_row := raster + page_width - 1;
  2329.     while (page[raster] = ' ') and (raster < next_row) do
  2330.       incr(raster) ;
  2331.     while (page[raster] <> ' ') and (raster < next_row) do
  2332.       incr(raster) ;
  2333.     jspace := raster ;
  2334.     while raster < next_row do
  2335.       begin
  2336.       while (page[raster] = ' ') and (raster < next_row) do
  2337.          incr(raster) ;
  2338.       shift := raster - jspace - 1 ;
  2339.       while (page[raster] <> ' ') and (raster < next_row) do
  2340.         begin page[raster-shift] := page[raster] ;
  2341.         incr(raster) ;
  2342.         end ;
  2343.       for k := raster - shift to raster - 1 do page[k] := ' ' ;
  2344.       jspace := raster - shift ;
  2345.       end ;
  2346.     end ;
  2347. last_hh := min(last_hh,jspace);
  2348. @ When the portion of the page is printed, either the interactive user will be
  2349. given a menu of options to chose from or the batch job will continue.
  2350. @d null = 0
  2351. @d left_arrow = 1
  2352. @d right_arrow = 2
  2353. @d up_arrow = 3
  2354. @d down_arrow = 4
  2355. @d comp = 5
  2356. @d next_page = 6
  2357. @<Determine page action@> =
  2358. begin if batch_mode then
  2359.   @<Draw graded horizontal rule@>
  2360. else begin last_p_row := i-1 ;
  2361.    @<Select from menu@>;
  2362.    case menu of
  2363.       left_arrow: begin first_p_hh := first_p_hh - print_width + hor_overlap;
  2364.                   if first_p_hh < min_hh then first_p_hh := min_hh;
  2365.                   end;
  2366.       right_arrow: begin first_p_hh := last_p_hh - hor_overlap;
  2367.                    if first_p_hh > last_hh - print_width then
  2368.                       first_p_hh := last_hh - print_width ;
  2369.                    end;
  2370.       up_arrow: begin k := 0;
  2371.                 while (k < print_height-vert_overlap) and
  2372.                       (first_p_row > min_vv) do
  2373.                   begin first_p_row := first_p_row - 1 ;
  2374.                     j := line_for[first_p_row] ;
  2375.                     if (j >= 0) and (priority[j] ='b') then incr(k) ;
  2376.                   end;
  2377.                 end;
  2378.       down_arrow: if last_p_row = last_vv then
  2379.                     printing_page := false
  2380.                   else
  2381.                     begin first_p_row := last_p_row - vert_overlap;
  2382.                     if first_p_row > last_vv - print_height then
  2383.                       first_p_row := last_vv - print_height + 1;
  2384.                     end;
  2385.       comp: if (not compress) then @<Compress spaces between words@>;
  2386.       next_page: @<Draw graded horizontal rule@>;
  2387.       othercases @<Draw graded horizontal rule@>
  2388.       endcases
  2389.    end;
  2390. @ The interactive user will be given a choice of re-drawing the page, shifted
  2391. right, left, up, or down, or in the compressed mode.  Any other selection will
  2392. cause the process to go to the next page.
  2393. @  @<Select from menu@> =
  2394. begin rewrite(term_out); write(term_out,'Hit key for page action.. ');
  2395. input_ln;
  2396. buf_ptr :=0;
  2397. menu := null;
  2398. if (buffer[buf_ptr] = u_left) or (buffer[buf_ptr] = l_left) then
  2399.    menu := left_arrow
  2400. else if (buffer[buf_ptr] = u_right) or (buffer[buf_ptr] = l_right) then
  2401.    menu := right_arrow
  2402. else if (buffer[buf_ptr] = u_up) or (buffer[buf_ptr] = l_up) then
  2403.    menu := up_arrow
  2404. else if (buffer[buf_ptr] = u_page) or (buffer[buf_ptr] = l_page) then
  2405.    menu := next_page
  2406. else if (buffer[buf_ptr] = u_comp) or (buffer[buf_ptr] = l_comp) then
  2407.    menu := comp
  2408. else menu := down_arrow;
  2409. incr(buf_ptr);
  2410. @ At the end of each page, a graded horizontal rule will be drawn.  The
  2411. gradations will be every 2 cm. measured from point with dvi value 0.
  2412. @<Draw graded horizontal rule@> =
  2413. begin write_ln(bit_file);
  2414. position := trunc(200000.0/num*den/resol) ;
  2415. write(bit_file,' _ cm --> ');
  2416. i := 0;
  2417. while i <= last_p_hh do
  2418.    begin
  2419.       if i > first_p_hh + 9 then
  2420.          if i mod position = 0 then
  2421.             begin
  2422.                if 2*(i div position) < 10 then
  2423.                   write(bit_file,(2*(i div position)):1,'_')
  2424.                else
  2425.                   write(bit_file,(2*(i div position)):2);
  2426.             incr (i);
  2427.             end
  2428.          else write(bit_file,'_') ;
  2429.       incr (i);
  2430.    end;
  2431. write_ln(bit_file);
  2432. printing_page := false;
  2433. @ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
  2434. so we shall split |do_page| into two parts, one of which is
  2435. called |spcl_cases|. The different parts communicate with each other
  2436. via the global variables mentioned above, together with the following ones:
  2437. @<Glob...@>=
  2438. @!s:integer; {current stack size}
  2439. @!cur_font:integer; {current internal font number}
  2440. @!a:integer; {byte number of the current command}
  2441. @ Here is the overall setup.
  2442. @p @t\4@>@<Declare the function called |spcl_cases|@>@;
  2443. function do_page:boolean;
  2444. label fin_set,fin_rule,move_right,show_state,done,9998,9999;
  2445. var o:eight_bits; {operation code of the current command}
  2446. @!p,@!q:integer; {parameters of the current command}
  2447. @!hhh:integer; {|h|, rounded to the nearest pixel}
  2448. begin start_page; cur_font:=0; {set current font undefined}
  2449. while mem[cur_font]>0 do cur_font := cur_font + 1 ;
  2450. s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0;
  2451. hh:=raster_round(0); vv:=line_round(0);
  2452.   {initialize the state variables}
  2453. cur_fptr := 0 ; set_word := false ;
  2454. while true do @<Translate the next command in the \.{DVI} file;
  2455.     |goto 9999| with |do_page=true| if it was |eop|;
  2456.     |goto 9998| if premature termination is needed@>;
  2457. 9998: print_ln('!'); do_page:=false;
  2458. 9999: end;
  2459. @ @<Translate the next command...@>=
  2460. begin a:=cur_loc ;
  2461. o:=get_byte; if o > 127 then p:=first_par(o);
  2462. if eof(dvi_file) then bad_dvi('the file ended prematurely');
  2463. @.the file ended prematurely@>
  2464. @<Start translation of command |o| and |goto| the appropriate label to
  2465.   finish the job@>;
  2466. fin_set: @<Finish a command that either sets or puts a character, then
  2467.     |goto move_right| or |done|@>;
  2468. fin_rule: @<Finish a command that either sets or puts a rule, then
  2469.     |goto move_right| or |done|@>;
  2470. move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
  2471. show_state: @<Show the values of |h|, |v|, |w|, |x|, |y|, |z|,
  2472.   |hh|, and |vv|; then |goto done|@>;
  2473. done:end
  2474. @ The multiway switch in |first_par|, above, was organized by the length
  2475. of each command; the one in |do_page| is organized by the semantics.
  2476. @<Start translation...@>=
  2477. if o<set_char_0+128 then draw_char(o)
  2478. else case o of
  2479.   four_cases(set1), four_cases(put1): begin draw_char(o); goto fin_set;
  2480.     end;
  2481.   set_rule, put_rule: begin q:=signed_quad ; dev_rule_draw(round(p/vresol),
  2482.             round(q/resol)); goto fin_rule;
  2483.           end;
  2484.   @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
  2485.   @t\4@>@<Cases for horizontal motion@>@;
  2486.   othercases if spcl_cases(o,p) then goto done@+else goto 9998
  2487. endcases ;
  2488. @ @<Declare the function called |spcl_cases|@>=
  2489. function spcl_cases(@!o:eight_bits;@!p:integer):boolean;
  2490. label change_font,move_down,done,9998;
  2491. var q:integer; {parameter of the current command}
  2492. @!k:integer; {loop index}
  2493. @!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
  2494. @!pure:boolean; {is the command error-free?}
  2495. @!vvv:integer; {|v|, rounded to the nearest raster}
  2496. begin pure:=true;
  2497. case o of
  2498. @t\4@>@<Cases for vertical motion@>@;
  2499. @t\4@>@<Cases for fonts@>@;
  2500. four_cases(xxx1): begin @<Translate an |xxx| command@>; goto done; end;
  2501. pre: begin print_ln('preamble command within a page!'); goto 9998;
  2502.   end;
  2503. @.preamble command within a page@>
  2504. post,post_post: begin print_ln('postamble command within a page!'); goto 9998;
  2505. @.postamble command within a page@>
  2506.   end;
  2507. othercases begin print_ln('undefined command ',o:1,'!');
  2508.   goto done;
  2509. @.undefined command@>
  2510.   end
  2511. endcases;
  2512. move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
  2513. change_font: @<Finish a command that changes the current font,
  2514.   then |goto done|@>;
  2515. 9998: pure:=false;
  2516. done: spcl_cases:=pure;
  2517. @ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
  2518. nop: goto done;
  2519. bop: begin print_ln('bop occurred before eop'); goto 9998;
  2520. @.bop occurred before eop@>
  2521.   end;
  2522. eop: begin finish_page ;
  2523.   if s<>0 then print_ln('stack not empty at end of page (level ',
  2524.     s:1,')!');
  2525. @.stack not empty...@>
  2526.   do_page:=true; goto 9999;
  2527.   end;
  2528. push: begin
  2529.   if s=max_s_so_far then
  2530.     begin max_s_so_far:=s+1;
  2531.     if s=max_s then print_ln('deeper than claimed in postamble!');
  2532. @.deeper than claimed...@>
  2533. @.push deeper than claimed...@>
  2534.     if s=stack_size then
  2535.       begin print_ln(clone, ' capacity exceeded (stack size=',
  2536.         stack_size:1,')'); goto 9998;
  2537.       end;
  2538.     end;
  2539.   hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
  2540.   xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
  2541.   hhstack[s]:=hh; vvstack[s]:=vv; incr(s); goto show_state;
  2542.   end;
  2543. pop: begin
  2544.   if s=0 then print_ln('(illegal at level zero)!')
  2545.   else  begin if set_word then reset_word ;
  2546.     decr(s);
  2547.     hh:=hhstack[s]; vv:=vvstack[s];
  2548.     h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
  2549.     x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
  2550.     end;
  2551.   goto show_state;
  2552.   end;
  2553. @ Our screen resolution is coarse, and we shall reset characters in words so
  2554. that the characters are left justified in the space \TeX\ provides.  We shall do
  2555. our position calculations in the same manner that they would be done on the
  2556. printer.  Rounding to the nearest raster is best done in the manner shown here,
  2557. so as to be inoffensive to the eye: When the horizontal motion is small, like a
  2558. kern, |hh| changes by rounding the kern; but when the motion is large, |hh|
  2559. changes by rounding the true position |h| so that accumulated rounding errors
  2560. disappear. We allow a larger space in the negative direction than in the
  2561. positive one, because \TeX\ makes comparatively large backspaces when it
  2562. positions accents.
  2563. Note that, if the horizontal movement is large, the word is reset.
  2564. @d out_space(#)==if cur_fptr=0 then hh := raster_round(h+p)
  2565.       else begin
  2566.          if (abs(p) >= round(vresol / 3)) and (set_word) then reset_word ;
  2567.          if (p>=font_space)or(p<=-4*font_space) then
  2568.             hh:=raster_round(h+p)
  2569.          else hh:=hh+round(p/resol);
  2570.          end;
  2571.       q:=p; hpos_ed := false ; goto move_right
  2572. @<Cases for horizontal motion@>=
  2573. four_cases(right1):begin out_space('right',o-right1+1:1);
  2574.   end;
  2575. w0,four_cases(w1):begin w:=p; out_space('w',o-w0:1);
  2576.   end;
  2577. x0,four_cases(x1):begin x:=p; out_space('x',o-x0:1);
  2578.   end;
  2579. @ Vertical motion is done similarly, but with the threshold between
  2580. ``small'' and ``large'' increased by a factor of five. The idea is to make
  2581. fractions like ``$1\over2$'' round consistently, but to absorb accumulated
  2582. rounding errors in the baseline-skip moves.
  2583. Again, if the the vertical movement is large, the word is reset.
  2584. @d out_vmove(#)==if cur_fptr=0 then vv := line_round(v+p)
  2585.    else begin
  2586.       if (p <> 0) and (set_word) then reset_word ;
  2587.       if abs(p)>=5*font_space then
  2588.          vv:=line_round(v+p)
  2589.       else vv:=vv+round(p/vresol);
  2590.       end;
  2591.    vpos_ed := false ;
  2592.  goto move_down
  2593. @<Cases for vertical motion@>=
  2594. four_cases(down1):begin out_vmove('down',o-down1+1:1);
  2595.   end;
  2596. y0,four_cases(y1):begin y:=p; out_vmove('y',o-y0:1);
  2597.   end;
  2598. z0,four_cases(z1):begin z:=p; out_vmove('z',o-z0:1);
  2599.   end;
  2600. @ @<Cases for fonts@>=
  2601. sixty_four_cases(fnt_num_0), four_cases(fnt1): goto change_font;
  2602. four_cases(fnt_def1): begin
  2603.    skip_font_def;
  2604.    goto done;
  2605.   end;
  2606. @ @<Finish a command that either sets or puts a character...@>=
  2607. q:=tfm_width ;
  2608. if o>=put1 then goto done;
  2609. hh:=hh+raster_width;
  2610. goto move_right
  2611. @ @<Finish a command that either sets or puts a rule...@>=
  2612. if o=put_rule then goto done;
  2613. hh:=hh+round(q/resol); goto move_right
  2614. @ A sequence of consecutive rules, or consecutive characters in a fixed-width
  2615. font whose width is not an integer number of rasters, can cause |hh| to drift
  2616. far away from a correctly rounded value. \vutex\ ensures that the
  2617. amount of drift will never exceed |max_drift| rasters.
  2618. @d infinity==@'17777777777 {$\infty$ (approximately)}
  2619. @d max_drift=1 {we insist that abs|(hh-raster_round(h))<=max_drift|}
  2620. @<Finish a command that sets |h:=h+q|, then |goto done|@>=
  2621. hhh:=raster_round(h+q);
  2622. if abs(hhh-hh)>max_drift then begin hpos_ed := false ;
  2623.   if hhh>hh then hh:=hhh-max_drift
  2624.   else hh:=hhh+max_drift;
  2625. end ;
  2626. h:=h+q;
  2627. if abs(h)>max_h_so_far then
  2628.   begin if abs(h)>max_h+99 then
  2629.     begin print_ln('warning: |h|>',max_h:1,'!');
  2630. @.warning: |h|...@>
  2631.     max_h:=abs(h);
  2632.   end;
  2633.   max_h_so_far:=abs(h);
  2634.   end;
  2635. goto done
  2636. @ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
  2637. if (v>0)and(p>0) then if v>infinity-p then
  2638.   begin print_ln('arithmetic overflow! parameter changed from ',
  2639. @.arithmetic overflow...@>
  2640.     p:1,' to ',infinity-v:1);
  2641.   p:=infinity-v;
  2642.   end;
  2643. if (v<0)and(p<0) then if -v>p+infinity then
  2644.   begin print_ln('arithmetic overflow! parameter changed from ',
  2645.     p:1, ' to ',(-v)-infinity:1);
  2646.   p:=(-v)-infinity;
  2647.   end;
  2648. vvv:=line_round(v+p);
  2649. if abs(vvv-vv)>max_drift then begin
  2650.   if vvv>vv then vv:=vvv-max_drift
  2651.   else vv:=vvv+max_drift;
  2652.   end;
  2653. v:=v+p;
  2654. if abs(v)>max_v_so_far then
  2655.   begin if abs(v)>max_v+99 then
  2656.     begin print_ln('warning: |v|>',max_v:1,'!');
  2657. @.warning: |v|...@>
  2658.     max_v:=abs(v);
  2659.     end;
  2660.   max_v_so_far:=abs(v);
  2661.   end;
  2662. goto done
  2663. @ @<Show the values of |h|, |v|, |w|, |x|, |y|, |z|...@>=
  2664. goto done
  2665. @ @<Finish a command that changes the current font...@>=
  2666. cur_fptr := mem[p] ;
  2667. goto done
  2668. @* Skipping pages.
  2669. A routine that's much simpler than |do_page| is used to pass over
  2670. pages that are not being translated. The |skip_pages| subroutine
  2671. is assumed to begin just after the preamble has been read, or just
  2672. after a |bop| has been processed. It continues until either finding a
  2673. |bop| that matches the desired starting page specifications, or until
  2674. running into the postamble.
  2675. @p procedure skip_pages;
  2676. label 9999; {end of this subroutine}
  2677. var p:integer; {a parameter}
  2678. @!k:0..255; {command code}
  2679. @!down_the_drain:integer; {garbage}
  2680. bad_char:boolean ;
  2681. begin while true do
  2682.   begin if eof(dvi_file) then bad_dvi('the file ended prematurely');
  2683. @.the file ended prematurely@>
  2684.   k:=get_byte;
  2685.   if k > 127 then begin p:=first_par(k);
  2686.   case k of
  2687.   bop: begin @<Pass a |bop| command, setting up the |count| array@>;
  2688.     if not started and start_match then
  2689.       begin started:=true; goto 9999;
  2690.       end;
  2691.     end;
  2692.   set_rule,put_rule: down_the_drain:=signed_quad;
  2693.   fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: if prescan then begin
  2694.        define_font(p);
  2695.     end else skip_font_def ;
  2696.   xxx1,xxx1+1,xxx1+2,xxx1+3: if prescan then begin
  2697.             @<Translate an |xxx| command@>
  2698.    end else
  2699.    while p>0 do
  2700.     begin down_the_drain:=get_byte; decr(p);
  2701.     end;
  2702.   post: begin in_postamble:=true; goto 9999;
  2703.     end;
  2704.   othercases do_nothing
  2705.   endcases;
  2706.   end ;
  2707.   end;
  2708. 9999:end;
  2709. @ Now we need a routine that will scan a page from the |bop| to the |eop|,
  2710. maintaining information about font usage.  This routine also catches errors
  2711. like illegal font numbers or characters.  It also counts the number of pages
  2712. actually processed.
  2713. @p function scan_page : boolean ;
  2714. label 9999; {end of this subroutine}
  2715. var p:integer; {a parameter}
  2716. @!k:0..255; {command code}
  2717.   bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
  2718. @!down_the_drain:integer; {garbage}
  2719. begin actual_page_count := actual_page_count + 1 ;
  2720.    while true do
  2721.    begin a:=cur_loc ;
  2722.     if eof(dvi_file) then bad_dvi('the file ended prematurely');
  2723. @.the file ended prematurely@>
  2724.   k:=get_byte;
  2725.   if eof(dvi_file) then bad_dvi('the file ended prematurely');
  2726. @.the file ended prematurely@>
  2727.   if k<set_char_0+128 then begin
  2728.       cur_char_ptr := cur_fptr + 2*k ;
  2729.       incr(use_count);
  2730.   end else begin
  2731.   p:=first_par(k);
  2732.   case k of
  2733.   bop: begin @<Pass a |bop| command, setting up the |count| array@>;
  2734.     if not started and start_match then
  2735.       begin started:=true; goto 9999;
  2736.       end;
  2737.     end;
  2738.    eop: goto 9999 ;
  2739.    four_cases(set1),four_cases(put1): begin if p > 128 then
  2740.       abort('character greater than 128 encountered!')
  2741. @.character greater than...@>
  2742.       else begin cur_char_ptr := cur_fptr + 2*p ; incr(use_count); end; end ;
  2743.   set_rule,put_rule: down_the_drain:=signed_quad;
  2744.   fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: define_font(p);
  2745.   sixty_four_cases(fnt_num_0),four_cases(fnt1): begin
  2746.       if p>255 then abort(clone, ' cannot handle font numbers > 255!') ;
  2747. @.vutex capacity exceeded...@>
  2748.       cur_fptr := mem[p] ;
  2749.       if cur_fptr=0 then abort('Undefined font!') ;
  2750. @.Undefined font@>
  2751.       end ;
  2752.   four_cases(xxx1): @<Translate an |xxx| command@>
  2753.   post: begin in_postamble:=true; goto 9999;
  2754.     end;
  2755.   othercases do_nothing
  2756.   endcases;
  2757.   end ;
  2758.  end;
  2759. 9999:scan_page := true; end;
  2760. @ The global variable |started| indicates that we have found the
  2761. starting page.  |last_page| points to the most recent page processed,
  2762. and |prev_page| points to the page before it.  These two globals can be
  2763. used for processing a file in the reverse output, if the printer being
  2764. driven stacks the pages backwards, for instance.
  2765. @<Glob...@>=
  2766. @!started:boolean; {has the starting page been found?}
  2767. @!last_page: integer; {what is the most recent page processed?}
  2768. @!prev_page: integer; {what is the page before it?}
  2769. @!actual_page_count: integer; {how many pages to actually process}
  2770. @ @<Set init...@>=
  2771. started:=false;
  2772. last_page:=-1;
  2773. prev_page:=-1;
  2774. actual_page_count:=0;
  2775. @ @<Pass a |bop|...@>=
  2776. last_page:=cur_loc;
  2777. incr(page_count);
  2778. for k:=0 to 9 do count[k]:=signed_quad;
  2779. prev_page:=signed_quad ;  {skip over the back pointers}
  2780. @ We need a procedure to open the output file, and to send out any
  2781. initialization that the printer might need.
  2782. @<Open bit file and initialize device@>=
  2783. open_bit_file ; init_device ;
  2784. @* Page scanning routines.
  2785. Here are a few miscellaneous routines which will scan the page, calling
  2786. the |pre_scan| and |do_page| routines as necessary.
  2787. @<Process the preamble@>=
  2788. open_dvi_file;
  2789. p:=get_byte; {fetch the first byte}
  2790. if p<>pre then bad_dvi('First byte isn''t start of preamble!');
  2791. @.First byte isn't...@>
  2792. p:=get_byte; {fetch the identification byte}
  2793. if p<>id_byte then
  2794.   print_ln('identification in byte 1 should be ',id_byte:1,'!');
  2795. @.identification...should be n@>
  2796. @<Read the conversion units@>;
  2797. p:=get_byte; {fetch the length of the introductory comment}
  2798. print('''');
  2799. while p>0 do
  2800.   begin decr(p); print(xchr[get_byte]);
  2801.   end;
  2802. print_ln('''')
  2803. @ We also need a routine to skip over the preamble after the file has been
  2804. reset again.
  2805. @<Skip over the preamble@>=
  2806. reopen_dvi_file;
  2807. p:=get_byte; {fetch the first byte}
  2808. p:=get_byte; {fetch the identification byte}
  2809. p:=signed_quad; p:=signed_quad; p:=signed_quad; {skip over the mag stuff}
  2810. p:=get_byte; {fetch the length of the introductory comment}
  2811. while p>0 do begin
  2812.   i:=get_byte; decr(p); end;
  2813. @ Read in the conversion units and magnification for the font.
  2814. @<Glob...@>=
  2815. @!num,den : integer;  {numerator and denominator for conversion of dvi units}
  2816. @ @<Read the conversion units@>=
  2817. num:=signed_quad; if num<=0 then bad_dvi('numerator is ',num:1);
  2818. @.numerator is wrong@>
  2819. den:=signed_quad; if den<=0 then bad_dvi('denominator is ',den:1);
  2820. @.denominator is wrong@>
  2821. p:=signed_quad;
  2822. @ The code shown here uses a convention that has proved to be useful:
  2823. If the starting page was specified as, e.g., `\.{1.*.-5}', then
  2824. all page numbers in the file are displayed by showing the values of
  2825. counts 0, 1, and~2, separated by dots. Such numbers can, for example,
  2826. be displayed on the console of a printer when it is working on that
  2827. page.
  2828. @<Translate up to |max_pages| pages@>=
  2829. begin print('- Page ');
  2830.  while max_pages>0 do
  2831.   begin decr(max_pages);
  2832.   for k:=0 to start_vals do
  2833.     begin print('[',count[k]:1);
  2834.     if k<start_vals then print('.')
  2835.     else print ('] ');
  2836.   end ;
  2837.   if not do_page then bad_dvi('page ended unexpectedly');
  2838. @.page ended unexpectedly@>
  2839.   begin
  2840.   repeat k:=get_byte;
  2841.   if (k>=fnt_def1)and(k<fnt_def1+4) then
  2842.     begin p:=first_par(k); define_font(p); k:=nop;
  2843.     end;
  2844.   until k<>nop;
  2845.   if k=post then
  2846.     begin in_postamble:=true; goto done;
  2847.     end;
  2848.   if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop');
  2849. @.byte n is not bop@>
  2850.    @<Pass a |bop|...@>;
  2851.    end ;
  2852.   end;
  2853. done: end;
  2854. @ This routine will prescan the file, using the |scan_page| macro to get
  2855. the information out of each page.
  2856. @<Prescan up to |max_pages| pages@>=
  2857. begin while max_pages>0 do
  2858.   begin decr(max_pages);
  2859.   if not scan_page then bad_dvi('page ended unexpectedly');
  2860. @.page ended unexpectedly@>
  2861.   if in_postamble then goto pdone ;
  2862.   repeat k:=get_byte;
  2863.   if (k>=fnt_def1)and(k<fnt_def1+4) then
  2864.     begin p:=first_par(k); skip_font_def; k:=nop;
  2865.     end;
  2866.   until k<>nop;
  2867.   if k=post then
  2868.     begin in_postamble:=true; goto pdone;
  2869.     end;
  2870.   if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop',k);
  2871. @.byte n is not bop@>
  2872.   @<Pass a |bop|...@>;
  2873.   end;
  2874. pdone:end
  2875. @* Font loading.
  2876. Now we have the procedure which determines the vertical and
  2877. horizontal raster resolution.  On entrance to this procedure, all the font usage
  2878. information will have been loaded into the |use_count| entries for each font
  2879. descriptor.  Based on this, |base_font| will determine the resolution to use
  2880. for assigning screen raster positions.
  2881. First, this routine reads the file \.{nonASCII.tex.fnt} to determine which fonts
  2882. are non ASCII, i.e. not printable on the screen.  This list is shorter than the
  2883. list of ASCII fonts.
  2884. @p procedure base_font ;
  2885. var i, j, k : integer ;
  2886.     f_type : integer ; {type for listed fonts}
  2887.     lf,lh,bc,ec,nw,np : integer ; {help in decoding the \.{TFM} file}
  2888.     value, x : integer ; {a \.{TFM} |fix_word|}
  2889. @<|base_font| variables@>
  2890. begin
  2891.    @<Load non ASCII font information@> ;
  2892.    @<Determine base font@> ;
  2893. end ;
  2894. @ The non ASCII font information is assumed to be in the following format:
  2895. \medskip
  2896. {\def\ff#1\cr{\line{\hskip\parindent\tt #1\hfil}}
  2897. \ff amsy  2\cr
  2898. \ff ambsy  2\cr
  2899. \ff amcsc  2\cr
  2900. \ff amit  4\cr
  2901. \ff amtt  5\cr
  2902. \medskip
  2903. The only item on a line is the font name and the type code.
  2904. @d font_list_file=='nonASCII.tex.fnt' {change this to correct name}
  2905. @d list_len==16
  2906. @<Glob...@>=
  2907. @!resol, vresol : real;  {number of dvi units per raster or line}
  2908. @!gen_input : text_file ; {input path for text}
  2909. @!list_fonts : packed array [1..list_len] of char ; {non ASCII fonts name}
  2910. @!num_asc_fonts : integer ; {number of ASCII fonts declared}
  2911. @ @<Set init...@>=
  2912. list_fonts := font_list_file ;
  2913. @ @<Load non ASCII font information@>=
  2914. for k := 1 to name_length do cur_name[k] := ' ' ;
  2915. for k := 1 to list_len do
  2916.    if (list_fonts[k] <= 'z') and (list_fonts[k] >= 'a') then
  2917.       cur_name[k] := xchr[xord[list_fonts[k]]-@'40]
  2918.    else cur_name[k] := list_fonts[k] ;
  2919. open_input_text ;
  2920. num_asc_fonts := 0 ;
  2921. while not eof(gen_input) do
  2922.    @<Process line@> ;
  2923. @ At this point, we are at the beginning of a line in the non ASCII font
  2924. information file.  We read in the name of a non printable font, and then scan
  2925. through our fonts looking for a match.  By default all fonts are assumed to be
  2926. roman unless named in this file.
  2927. @<Process line@>=
  2928. begin
  2929.    buf_ptr := 1 ;
  2930.    while not eoln(gen_input) do begin
  2931.       buffer[buf_ptr] := xord[gen_input^] ;
  2932.       get(gen_input) ;
  2933.       buf_ptr := buf_ptr + 1 ;
  2934.    end ;
  2935.    read_ln (gen_input) ;
  2936.    buffer[buf_ptr] := " " ;
  2937.    buf_ptr := 1 ;
  2938.    while buffer[buf_ptr] <> " " do buf_ptr := buf_ptr + 1 ;
  2939.    while buffer[buf_ptr] = " " do buf_ptr := buf_ptr + 1 ;
  2940.    f_type := get_integer ;
  2941.    for i := 0 to 255 do if mem[i] > 0 then begin
  2942.       cur_fptr := mem[i] ;
  2943.       j := font_name ; k := 1 ;
  2944.       if names[j] = 0 then begin
  2945.          if (font_type)=declared then
  2946.             begin font_type := roman ;
  2947.             incr(num_asc_fonts) ;
  2948.             end ;
  2949.          j := j + 1 ;
  2950.          while (names[j] = buffer[k]) do begin
  2951.             j := j + 1 ; k := k + 1 ; end ;
  2952.          if (buffer[k] = " ") then begin
  2953.             font_type := f_type ;
  2954.             if f_type = other then decr(num_asc_fonts) ;
  2955.          end ;
  2956.       end ;
  2957.    end ;
  2958. @ This section of code figures out which font is to determine the resolution.
  2959. It goes through the list of fonts, and, for each font that is ASCII, it
  2960. calculates the |use_count| and the font number into a list built at the top of
  2961. memory.  This list is then sorted, and the fonts are given priorities.
  2962. Horizontal resolution normally is slightly less than the width of normal
  2963. interword space, except for fonts where this parameter is 0 in which case one
  2964. third of a quad is used.
  2965. The vertical resolution is determined by placing |num_lines| on the physical
  2966. page.
  2967. @d dd_s_k(#)==mem[temp_ar(#)-1] {sort key for advantages}
  2968. @<Determine base font@>=
  2969. @<Make list of declared fonts and calculate advantages@> ;
  2970. @<Sort by advantages and build linked list@> ;
  2971. @<Load the font data@> ;
  2972. @ Making a list of the declared fonts is relatively easy, as is calculating
  2973. the advantages.  The advantage is the use count for all fonts.
  2974. @<Make list of declared fonts...@>=
  2975. asc_num := 0 ;
  2976. nasc_num := num_asc_fonts ;
  2977. for i := 0 to 255 do if mem[i] > 0 then begin
  2978.    cur_fptr := mem[i] ;
  2979.    cur_char_ptr := cur_fptr ;
  2980.    dd_total := 0 ;
  2981.    for j := 0 to 127 do begin
  2982.       if use_count > 0 then
  2983.          dd_total := dd_total + use_count ;
  2984.       cur_char_ptr := cur_char_ptr + 2 ;
  2985.    end ;
  2986.    if (dd_total >0) then
  2987.      begin if (font_type > other) then
  2988.         begin temp_ar(asc_num) := cur_fptr ;
  2989.         incr(asc_num) ;
  2990.      end else if (font_type = other) then
  2991.         begin temp_ar(nasc_num) := cur_fptr ;
  2992.         incr(nasc_num) ;
  2993.      end ;
  2994.    end ;
  2995.    mem[cur_fptr + 1] := dd_total ;
  2996. @ The sort to determine the priority of the fonts is a simple insertion sort.
  2997. Since this sort is only performed once, and it usually has less than a dozen
  2998. elements to sort, simplicity is called for.  Note that the ASCII fonts are
  2999. sorted at the top, and the non-ASCII fonts are sorted last.
  3000. @<Sort by advantages and build linked list@>=
  3001. for i := asc_num-1 downto 1 do
  3002.    for j := 0 to i-1 do
  3003.       if mem[temp_ar(j)+1] < mem[temp_ar(i)+1] then begin
  3004.          t := temp_ar(i) ; temp_ar(i) := temp_ar(j) ; temp_ar(j) := t ;
  3005.       end ;
  3006. for i := nasc_num-1 downto num_asc_fonts+1 do
  3007.    for j := num_asc_fonts to i-1 do
  3008.       if mem[temp_ar(j)+1] < mem[temp_ar(i)+1] then begin
  3009.          t := temp_ar(i) ; temp_ar(i) := temp_ar(j) ; temp_ar(j) := t ;
  3010.       end ;
  3011. for j := 0 to asc_num-1 do dd_s_k(j) := j ;
  3012. for j := num_asc_fonts to nasc_num-1 do dd_s_k(j) := j ;
  3013. @ Some of |base_font|' locals:
  3014. @<|base_font| variables@>=
  3015. @!asc_num : integer ; {positions the ASCII fonts used}
  3016. @!nasc_num : integer ; {positions the non-ASCII fonts used}
  3017. @!t : integer ; {temporary holding place for exchange swaps}
  3018. @!dd_total : integer ; {contains total number of different characters}
  3019. @ We now will load the \.{TFM} file for each font.  We also will determine the
  3020. resolution from the |base_font| and will scale all \.{TFM} widths accordingly.
  3021. @<Load the font data@>=
  3022. cur_fptr := mem[next_mem_free] ;
  3023. load_tfm_file ; @<Determine vertical and horizontal resolution@> ;
  3024. for i := 0 to 255 do
  3025.    begin if (mem[i] > 0) then
  3026.       begin cur_fptr := mem[i] ;
  3027.       if font_status <> 0 then load_tfm_file ;
  3028.       cur_char_ptr := cur_fptr ;
  3029.       for j := 0 to 127 do begin
  3030.          if (use_count > 0) then begin
  3031.             raster_width := round(tfm_width/resol) ;
  3032.             if raster_width < 1 then raster_width := 1;
  3033.          end ;
  3034.          cur_char_ptr := cur_char_ptr + 2 ;
  3035.       end ;
  3036.    end ;
  3037. @ The vertical resolution is determined to place |num_lines| on a page of
  3038. dimension |page_length|.  The horizontal resolution is given by the inter-word
  3039. space for the |base_font|.  Note that this section assumes that the \.{TFM} data
  3040. for the |base_font| is exactly as it was initally read into |mem|.
  3041. @<Determine vertical and horizontal resolution@>=
  3042. begin lf := hi(mem[next_mem_free]) ; lh := lo(mem[next_mem_free]) ;
  3043.    bc := hi(mem[next_mem_free+1]) ; ec := lo(mem[next_mem_free+1]) ;
  3044.    nw := hi(mem[next_mem_free+2]) ; np := lo(mem[next_mem_free+5]) ;
  3045.    k := next_mem_free + 6 + lh - bc ;
  3046.    vresol := den / (num / 100000.0) * page_length / num_lines ;
  3047.    value := mem[next_mem_free + lf - np + 1] ;
  3048.    x := tfm_to_int(value) ;
  3049.    resol := x * 1.0 ;
  3050.    if resol <= 0 then
  3051.       begin value := mem[next_mem_free + lf - np + 5] ;
  3052.       x := tfm_to_int(value) ;
  3053.       resol := x / 3.0 ;
  3054.    end;
  3055. end ;
  3056. @ This routine may be used to initialize the output device to begin receiving
  3057. information.  This section could be personalized to a particular system.
  3058. @p procedure init_device ;
  3059. begin
  3060. end ;
  3061. @ Now for the antithesis of the above.  This routine cleans up the \TeX\
  3062. job, and prepares for the next.
  3063. @p procedure clean_printer ;
  3064. begin
  3065.    write_ln(bit_file);
  3066.    write_ln(bit_file,'The end...');
  3067.    write_ln;
  3068. end ;
  3069. @* The main program.
  3070. Now we are ready to put it all together. This is where \vutex\ starts,
  3071. and where it ends.
  3072. @p begin initialize; {get all variables initialized}
  3073. dialog; {set up all the options}
  3074. prescan := true ;
  3075. @<Process the preamble@>;
  3076. skip_pages;
  3077. if not in_postamble then
  3078.    @<Prescan up to |max_pages| pages@>;
  3079. @<Open bit file and initialize device@>;
  3080. base_font ;
  3081. prescan:=false ;
  3082. in_postamble := false ;
  3083. started:=false ;
  3084. max_pages:=actual_page_count ;
  3085. @<Skip over the preamble@>;
  3086. skip_pages;
  3087. @<Translate up to |max_pages| pages@>;
  3088. final_end:
  3089. if bit_is_open then clean_printer;
  3090. diagnostics;
  3091. @ The main program needs a few global variables in order to do its work.
  3092. @<Glob...@>=
  3093. @!k,@!p,@!q:integer; {general purpose registers}
  3094. @!prescan:boolean;  {indicates that we are in prescan phase}
  3095. @!bit_is_open : boolean ; {indicates that output file was opened.}
  3096. @ @<Set init...@>=
  3097. bit_is_open := false ;
  3098. @* System-dependent changes.
  3099. This section should be replaced, if necessary, by changes to the program
  3100. that are necessary to make \vutex\ work at a particular installation.
  3101. Any additional routines should be inserted here.
  3102. @^system dependencies@>
  3103. @* Index.
  3104. Pointers to error messages appear here together with the section numbers
  3105. where each ident\-i\-fier is used.
  3106.